<?php

namespace Tests\Feature;

use App\Models\MenuGroup;
use App\Models\MenuList;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;

class MenuAccessTest extends TestCase
{
    use RefreshDatabase;

    public function test_staff_only_receives_viewable_menus_for_active_tenant(): void
    {
        [$user, $tenant] = $this->staffWithTicketPermission();
        Sanctum::actingAs($user);

        $response = $this->withHeader('X-Tenant-ID', $tenant->id)
            ->getJson('/api/me/menus');

        $response
            ->assertOk()
            ->assertJsonPath('data.tenant_id', $tenant->id)
            ->assertJsonPath('data.access_level', 'staff')
            ->assertJsonPath('data.menus.0.slug', 'ticketing-ticket');
    }

    public function test_user_cannot_select_a_tenant_they_do_not_belong_to(): void
    {
        [$user] = $this->staffWithTicketPermission();
        $otherTenant = Tenant::create([
            'tenant_code' => 'OTHER',
            'tenant_name' => 'Other Tenant',
        ]);
        Sanctum::actingAs($user);

        $this->withHeader('X-Tenant-ID', $otherTenant->id)
            ->getJson('/api/me/menus')
            ->assertForbidden()
            ->assertJsonPath('message', 'User tidak memiliki akses ke tenant yang dipilih.');
    }

    public function test_tenant_owner_cannot_promote_a_user_to_master_admin(): void
    {
        [$owner, $tenant] = $this->staffWithTicketPermission('tenant_owner');
        $target = User::factory()->create(['access_level' => 'staff']);
        $target->userTenants()->create([
            'tenant_id' => $tenant->id,
            'is_default' => true,
        ]);

        $usersMenu = MenuList::create([
            'name' => 'Data Pengguna',
            'slug' => 'users-user',
            'url' => '/users',
            'is_active' => true,
        ]);
        $group = $owner->userMenuGroups()->first()->menuGroup;
        $group->menus()->attach($usersMenu->id, [
            'can_view' => true,
            'can_update' => true,
        ]);
        Sanctum::actingAs($owner);

        $this->withHeader('X-Tenant-ID', $tenant->id)
            ->patchJson("/api/users/{$target->id}", [
                'access_level' => 'master_admin',
            ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors('access_level');
    }

    public function test_index_response_uses_the_same_paginator_contract_as_tickets(): void
    {
        $master = User::factory()->create([
            'is_master' => true,
            'access_level' => 'master_admin',
        ]);
        Tenant::create([
            'tenant_code' => 'PAGINATED',
            'tenant_name' => 'Pagination Test',
        ]);
        Sanctum::actingAs($master);

        $this->getJson('/api/tenants?per_page=10')
            ->assertOk()
            ->assertJsonStructure([
                'success',
                'code',
                'message',
                'data' => [
                    'current_page',
                    'data',
                    'first_page_url',
                    'from',
                    'last_page',
                    'last_page_url',
                    'links',
                    'next_page_url',
                    'path',
                    'per_page',
                    'prev_page_url',
                    'to',
                    'total',
                ],
            ])
            ->assertJsonPath('data.current_page', 1)
            ->assertJsonPath('data.per_page', 10)
            ->assertJsonPath('data.data.0.tenant_code', 'PAGINATED');
    }

    public function test_index_query_parameters_are_validated(): void
    {
        $master = User::factory()->create([
            'is_master' => true,
            'access_level' => 'master_admin',
        ]);
        Sanctum::actingAs($master);

        $this->getJson('/api/tenants?page=0&per_page=101&status=unknown')
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['page', 'per_page', 'status']);
    }

    public function test_tenant_owner_only_receives_tenants_assigned_to_the_user(): void
    {
        [$owner, $assignedTenant] = $this->staffWithTicketPermission('tenant_owner');
        $secondAssignedTenant = Tenant::create([
            'tenant_code' => 'OWNER-SECOND',
            'tenant_name' => 'Second Assigned Tenant',
        ]);
        $owner->userTenants()->create([
            'tenant_id' => $secondAssignedTenant->id,
            'is_default' => false,
        ]);
        $otherTenant = Tenant::create([
            'tenant_code' => 'OWNER-OTHER',
            'tenant_name' => 'Unassigned Tenant',
        ]);
        Sanctum::actingAs($owner);

        $response = $this->withHeader('X-Tenant-ID', $assignedTenant->id)
            ->getJson('/api/tenants?per_page=100')
            ->assertOk();

        $tenantIds = collect($response->json('data.data'))->pluck('id');

        $this->assertTrue($tenantIds->contains($assignedTenant->id));
        $this->assertTrue($tenantIds->contains($secondAssignedTenant->id));
        $this->assertFalse($tenantIds->contains($otherTenant->id));

        $this->withHeader('X-Tenant-ID', $assignedTenant->id)
            ->getJson("/api/tenants/{$secondAssignedTenant->id}")
            ->assertOk();

        $this->withHeader('X-Tenant-ID', $assignedTenant->id)
            ->getJson("/api/tenants/{$otherTenant->id}")
            ->assertForbidden();
    }

    public function test_authenticated_user_can_view_and_update_only_their_own_profile(): void
    {
        $user = User::factory()->create([
            'name' => 'Nama Lama',
            'access_level' => 'staff',
        ]);
        Sanctum::actingAs($user);

        $this->getJson('/api/me/profile')
            ->assertOk()
            ->assertJsonPath('data.id', $user->id)
            ->assertJsonPath('data.access_level', 'staff');

        $this->putJson('/api/me/profile', [
            'name' => 'Nama Baru',
            'username' => 'nama.baru',
            'email' => $user->email,
            'whatsapp_number' => '08123456789',
            'access_level' => 'master_admin',
            'is_master' => true,
            'user_profile' => [
                'nik' => '1234567890',
                'address' => 'Alamat baru',
            ],
        ])
            ->assertOk()
            ->assertJsonPath('data.name', 'Nama Baru')
            ->assertJsonPath('data.user_profile.nik', '1234567890')
            ->assertJsonPath('data.access_level', 'staff')
            ->assertJsonPath('data.is_master', false);

        $user->refresh();
        $this->assertSame('staff', $user->access_level->value);
        $this->assertFalse($user->is_master);
    }

    public function test_tenant_owner_creates_and_only_lists_groups_in_the_active_tenant(): void
    {
        [$owner, $tenant] = $this->staffWithTicketPermission('tenant_owner');
        $otherTenant = Tenant::create([
            'tenant_code' => 'OTHER-GROUP',
            'tenant_name' => 'Other Group Tenant',
        ]);
        $otherTenantGroup = MenuGroup::create([
            'tenant_id' => $otherTenant->id,
            'name' => 'Other Tenant Group',
            'is_active' => true,
        ]);
        $globalSystemGroup = MenuGroup::create([
            'tenant_id' => null,
            'name' => 'Global System Group',
            'is_system' => true,
            'is_active' => true,
        ]);
        $menu = MenuList::create([
            'name' => 'Group Menus',
            'slug' => 'users-group-menus',
            'url' => '/group-menus',
            'is_active' => true,
        ]);
        $owner->userMenuGroups()->first()->menuGroup->menus()->attach($menu->id, [
            'can_view' => true,
            'can_create' => true,
        ]);
        Sanctum::actingAs($owner);

        $this->withHeader('X-Tenant-ID', $tenant->id)
            ->postJson('/api/menu-groups', [
                'name' => 'Teknisi Tenant',
                'description' => 'Akses teknisi',
                'is_active' => true,
            ])
            ->assertCreated()
            ->assertJsonPath('data.tenant_id', $tenant->id)
            ->assertJsonPath('data.is_system', false)
            ->assertJsonPath('data.tenant.id', $tenant->id);

        $response = $this->withHeader('X-Tenant-ID', $tenant->id)
            ->getJson('/api/menu-groups?per_page=100')
            ->assertOk();

        $this->assertNotContains(
            'Other Tenant Group',
            collect($response->json('data.data'))->pluck('name')->all()
        );
        $this->assertNotContains(
            'Global System Group',
            collect($response->json('data.data'))->pluck('name')->all()
        );

        $this->withHeader('X-Tenant-ID', $tenant->id)
            ->getJson("/api/menu-groups/{$otherTenantGroup->id}")
            ->assertForbidden();

        $this->withHeader('X-Tenant-ID', $tenant->id)
            ->getJson("/api/menu-groups/{$globalSystemGroup->id}")
            ->assertForbidden();
    }

    public function test_master_admin_can_create_global_and_tenant_menu_groups(): void
    {
        $master = User::factory()->create([
            'is_master' => true,
            'access_level' => 'master_admin',
        ]);
        $tenant = Tenant::create([
            'tenant_code' => 'MASTER-SCOPE',
            'tenant_name' => 'Master Scope Tenant',
        ]);
        Sanctum::actingAs($master);

        $this->postJson('/api/menu-groups', [
            'name' => 'Global System Group',
            'is_active' => true,
            'tenant_id' => null,
        ])
            ->assertCreated()
            ->assertJsonPath('data.tenant_id', null)
            ->assertJsonPath('data.is_system', true);

        $this->postJson('/api/menu-groups', [
            'name' => 'Tenant Scoped Group',
            'is_active' => true,
            'tenant_id' => $tenant->id,
        ])
            ->assertCreated()
            ->assertJsonPath('data.tenant_id', $tenant->id)
            ->assertJsonPath('data.is_system', false)
            ->assertJsonPath('data.tenant.tenant_name', 'Master Scope Tenant');
    }

    private function staffWithTicketPermission(string $level = 'staff'): array
    {
        $tenant = Tenant::create([
            'tenant_code' => fake()->unique()->bothify('TEN-###'),
            'tenant_name' => fake()->company(),
        ]);
        $user = User::factory()->create(['access_level' => $level]);
        $user->userTenants()->create([
            'tenant_id' => $tenant->id,
            'is_default' => true,
        ]);
        $menu = MenuList::create([
            'name' => 'Tiket',
            'slug' => 'ticketing-ticket',
            'url' => '/tickets',
            'is_active' => true,
        ]);
        $group = MenuGroup::create([
            'tenant_id' => $tenant->id,
            'name' => 'Staff '.$tenant->id,
            'is_active' => true,
        ]);
        $group->menus()->attach($menu->id, [
            'can_view' => true,
            'can_update' => true,
        ]);
        $user->userMenuGroups()->create([
            'menu_group_id' => $group->id,
            'tenant_id' => $tenant->id,
        ]);

        return [$user, $tenant];
    }
}
