<?php

namespace Tests\Feature;

use App\Models\AuditLog;
use App\Models\Branch;
use App\Models\Business;
use App\Models\Customer;
use App\Models\Role;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Route;
use Tests\TestCase;

class StrictBranchIsolationTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed(DatabaseSeeder::class);

        Route::middleware(['web', 'auth'])->group(function (): void {
            Route::get('/_branch-isolation/customer/{customer}', function (Customer $customer) {
                return response()->json(['id' => $customer->id]);
            });

            Route::get('/_branch-isolation/staff/{user}', function (User $user) {
                return response()->json(['id' => $user->id]);
            });

            Route::post('/_branch-isolation/write', function () {
                return response()->noContent();
            });
        });
    }

    public function test_branch_user_only_lists_and_opens_records_from_assigned_branch(): void
    {
        [$branchA, $branchB] = $this->branches();
        $user = $this->branchAdministrator($branchA);
        $customerA = Customer::factory()->create(['branch_id' => $branchA->id, 'customer_number' => 'CUS-BRANCH-A']);
        $customerB = Customer::factory()->create(['branch_id' => $branchB->id, 'customer_number' => 'CUS-BRANCH-B']);

        $this->actingAs($user)
            ->get(route('customers.index'))
            ->assertOk()
            ->assertSee('CUS-BRANCH-A')
            ->assertDontSee('CUS-BRANCH-B');

        $this->actingAs($user)
            ->get('/_branch-isolation/customer/'.$customerA->id)
            ->assertOk();

        $this->actingAs($user)
            ->get('/_branch-isolation/customer/'.$customerB->id)
            ->assertForbidden();

        $this->actingAs($user)
            ->get(route('customers.edit', $customerB))
            ->assertForbidden();
    }

    public function test_branch_user_cannot_submit_create_or_update_scope_for_another_branch(): void
    {
        [$branchA, $branchB] = $this->branches();
        $user = $this->branchAdministrator($branchA);

        $this->actingAs($user)
            ->post('/_branch-isolation/write', ['branch_id' => $branchA->id])
            ->assertNoContent();

        $this->actingAs($user)
            ->post('/_branch-isolation/write', ['branch_id' => $branchB->id])
            ->assertForbidden();

        $this->actingAs($user)
            ->post('/_branch-isolation/write', ['branch_ids' => [$branchA->id, $branchB->id]])
            ->assertForbidden();

        $this->actingAs($user)
            ->post('/_branch-isolation/write', ['assignment' => ['primary_branch_id' => $branchB->id]])
            ->assertForbidden();

        $this->actingAs($user)
            ->post(route('customers.store'), ['branch_id' => $branchB->id])
            ->assertForbidden();
    }

    public function test_staff_records_and_activity_logs_are_isolated_by_branch(): void
    {
        [$branchA, $branchB] = $this->branches();
        $manager = $this->branchAdministrator($branchA);
        $staffA = $this->staff($branchA, 'staff.branch.a');
        $staffB = $this->staff($branchB, 'staff.branch.b');

        AuditLog::create(['user_id' => $staffA->id, 'branch_id' => $branchA->id, 'action' => 'branch.a.activity']);
        AuditLog::create(['user_id' => $staffB->id, 'branch_id' => $branchB->id, 'action' => 'branch.b.activity']);

        $this->actingAs($manager)
            ->get('/_branch-isolation/staff/'.$staffA->id)
            ->assertOk();

        $this->actingAs($manager)
            ->get('/_branch-isolation/staff/'.$staffB->id)
            ->assertForbidden();

        $this->actingAs($manager)
            ->get(route('admin.audit.index'))
            ->assertOk()
            ->assertSee('branch.a.activity')
            ->assertDontSee('branch.b.activity');
    }

    public function test_primary_branch_remains_accessible_when_legacy_pivot_assignment_is_missing(): void
    {
        [$branchA, $branchB] = $this->branches();
        $user = $this->branchAdministrator($branchA);
        $user->branches()->detach();

        $customerA = Customer::factory()->create([
            'branch_id' => $branchA->id,
            'customer_number' => 'CUS-LEGACY-A',
        ]);
        $customerB = Customer::factory()->create([
            'branch_id' => $branchB->id,
            'customer_number' => 'CUS-LEGACY-B',
        ]);

        $this->assertTrue($user->canAccessBranch($branchA->id));
        $this->assertFalse($user->canAccessBranch($branchB->id));

        $this->actingAs($user)
            ->get(route('customers.index'))
            ->assertOk()
            ->assertSee('CUS-LEGACY-A')
            ->assertDontSee('CUS-LEGACY-B');

        $this->actingAs($user)
            ->get('/_branch-isolation/customer/'.$customerA->id)
            ->assertOk();

        $this->actingAs($user)
            ->get('/_branch-isolation/customer/'.$customerB->id)
            ->assertForbidden();
    }

    public function test_super_administrator_retains_access_to_all_branches(): void
    {
        [$branchA, $branchB] = $this->branches();
        $admin = User::factory()->create(['username' => 'system.superadmin', 'status' => 'active']);
        $admin->roles()->attach(Role::where('name', 'super-administrator')->firstOrFail());
        $customerB = Customer::factory()->create(['branch_id' => $branchB->id]);

        $this->actingAs($admin)
            ->get('/_branch-isolation/customer/'.$customerB->id)
            ->assertOk();

        $this->actingAs($admin)
            ->post('/_branch-isolation/write', ['branch_ids' => [$branchA->id, $branchB->id]])
            ->assertNoContent();
    }

    /** @return array{0: Branch, 1: Branch} */
    private function branches(): array
    {
        $business = Business::firstOrFail();

        return [
            Branch::create(['business_id' => $business->id, 'name' => 'Branch A', 'status' => 'active']),
            Branch::create(['business_id' => $business->id, 'name' => 'Branch B', 'status' => 'active']),
        ];
    }

    private function branchAdministrator(Branch $branch): User
    {
        $user = $this->staff($branch, 'branch.admin.'.$branch->id);
        $user->roles()->attach(Role::where('name', 'branch-administrator')->firstOrFail());

        return $user;
    }

    private function staff(Branch $branch, string $username): User
    {
        $user = User::factory()->create([
            'primary_branch_id' => $branch->id,
            'username' => $username,
            'status' => 'active',
        ]);
        $user->branches()->sync([$branch->id => ['is_primary' => true]]);

        return $user;
    }
}
