<?php

namespace Tests\Feature\Foundation;

use App\Models\Branch;
use App\Models\Business;
use App\Models\Role;
use App\Models\User;
use Database\Seeders\AuthorizationSeeder;
use Database\Seeders\BusinessSeeder;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;

class StepOneFoundationTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_default_administrator_seeder_is_idempotent_and_assigns_super_admin(): void
    {
        $this->seed(DatabaseSeeder::class);
        $this->seed(DatabaseSeeder::class);

        $this->assertSame(1, User::where('email', 'admin@microfinance.local')->count());
        $admin = User::where('email', 'admin@microfinance.local')->firstOrFail();
        $this->assertTrue($admin->hasRole('super-administrator'));
        $this->assertTrue($admin->can('business.settings.update'));
        $this->assertNotNull($admin->email_verified_at);
    }

    public function test_active_user_can_login_with_username(): void
    {
        $user = User::factory()->create([
            'username' => 'loanofficer1',
            'employee_number' => 'EMP-001',
            'status' => 'active',
            'password' => Hash::make('Secret@12345'),
        ]);

        $response = $this->post(route('login.store'), [
            'login' => 'loanofficer1',
            'password' => 'Secret@12345',
        ]);

        $response->assertRedirect(route('dashboard'));
        $this->assertAuthenticatedAs($user);
        $this->assertDatabaseHas('audit_logs', ['user_id' => $user->id, 'action' => 'auth.login']);
    }

    public function test_inactive_user_cannot_login(): void
    {
        User::factory()->create([
            'username' => 'inactive1',
            'employee_number' => 'EMP-002',
            'status' => 'inactive',
            'password' => Hash::make('Secret@12345'),
        ]);

        $this->post(route('login.store'), ['login' => 'inactive1', 'password' => 'Secret@12345'])
            ->assertSessionHasErrors('login');

        $this->assertGuest();
    }

    public function test_user_without_permission_cannot_open_branch_management(): void
    {
        $user = User::factory()->create(['status' => 'active']);

        $this->actingAs($user)->get(route('admin.branches.index'))->assertForbidden();
    }

    public function test_super_administrator_can_access_all_foundation_administration(): void
    {
        $this->seed(DatabaseSeeder::class);
        $admin = User::where('email', 'admin@microfinance.local')->firstOrFail();

        $this->actingAs($admin)->get(route('admin.business-settings.edit'))->assertOk();
        $this->actingAs($admin)->get(route('admin.system-settings.edit'))->assertOk();
        $this->actingAs($admin)->get(route('admin.branches.index'))->assertOk();
        $this->actingAs($admin)->get(route('admin.users.index'))->assertOk();
        $this->actingAs($admin)->get(route('admin.roles.index'))->assertOk();
        $this->actingAs($admin)->get(route('admin.audit.index'))->assertOk();
    }

    public function test_branch_code_is_generated_by_system_and_cannot_be_overridden(): void
    {
        $this->seed(DatabaseSeeder::class);
        $admin = User::where('email', 'admin@microfinance.local')->firstOrFail();
        $business = Business::firstOrFail();

        $this->actingAs($admin)->post(route('admin.branches.store'), [
            'business_id' => $business->id,
            'name' => 'Mwanza',
            'code' => 'MANUAL-001',
            'status' => 'active',
        ])->assertRedirect(route('admin.branches.index'));

        $branch = Branch::where('name', 'Mwanza')->firstOrFail();
        $this->assertMatchesRegularExpression('/^BR-[A-Z0-9]{6}$/', $branch->code);
        $this->assertNotSame('MANUAL-001', $branch->code);
        $originalCode = $branch->code;

        $this->actingAs($admin)->put(route('admin.branches.update', $branch), [
            'business_id' => $business->id,
            'name' => 'Mwanza City',
            'code' => 'CHANGED-001',
            'status' => 'active',
        ])->assertRedirect(route('admin.branches.index'));

        $this->assertSame($originalCode, $branch->fresh()->code);
        $this->assertDatabaseHas('audit_logs', ['action' => 'branch.created', 'auditable_id' => $branch->id]);
    }

    public function test_branch_administrator_role_handles_all_branch_operations_without_global_admin_access(): void
    {
        $role = Role::where('name', 'branch-administrator')->firstOrFail();
        $this->assertSame('Branch Administrator (All Operations)', $role->display_name);

        foreach (['customers.create', 'loans.create', 'loans.approve', 'payments.receive', 'accounts.manage', 'expenses.manage', 'collections.manage', 'users.create', 'audit.view'] as $permission) {
            $this->assertTrue($role->permissions()->where('name', $permission)->exists(), "Missing {$permission}");
        }

        $this->assertFalse($role->permissions()->where('name', 'business.settings.update')->exists());
        $this->assertFalse($role->permissions()->where('name', 'system.settings.manage')->exists());
        $this->assertFalse($role->permissions()->where('name', 'roles.update')->exists());
    }

    public function test_staff_can_be_created_with_only_required_fields(): void
    {
        $this->seed(DatabaseSeeder::class);
        $admin = User::where('email', 'admin@microfinance.local')->firstOrFail();
        $business = Business::firstOrFail();
        $branch = Branch::create(['business_id' => $business->id, 'name' => 'Singida', 'status' => 'active']);

        $this->actingAs($admin)->post(route('admin.users.store'), [
            'name' => 'Single Branch Employee',
            'position' => 'Branch Officer',
            'phone' => '0712000000',
            'primary_branch_id' => $branch->id,
            'username' => 'singida_officer',
            'password' => 'Secret@12345',
            'employee_number' => 'MANUAL-EMPLOYEE-NO',
        ])->assertRedirect(route('admin.users.index'));

        $staff = User::where('username', 'singida_officer')->firstOrFail();
        $expectedEmployeeNumber = 'EMP-'.str_pad((string) $staff->id, 6, '0', STR_PAD_LEFT);

        $this->assertNull($staff->email);
        $this->assertSame($expectedEmployeeNumber, $staff->employee_number);
        $this->assertNotSame('MANUAL-EMPLOYEE-NO', $staff->employee_number);
        $this->assertSame('active', $staff->status);
        $this->assertSame($branch->id, $staff->primary_branch_id);
        $this->assertTrue($staff->branches()->whereKey($branch->id)->exists());
        $this->assertSame(0, $staff->roles()->count());

        $this->actingAs($admin)->put(route('admin.users.update', $staff), [
            'name' => $staff->name,
            'position' => $staff->position,
            'phone' => $staff->phone,
            'primary_branch_id' => $branch->id,
            'username' => $staff->username,
            'employee_number' => 'CHANGED-EMPLOYEE-NO',
        ])->assertRedirect(route('admin.users.index'));

        $this->assertSame($expectedEmployeeNumber, $staff->fresh()->employee_number);
    }

    public function test_branch_manager_is_restricted_to_assigned_branch(): void
    {
        $business = Business::firstOrFail();
        $branchA = Branch::create(['business_id' => $business->id, 'name' => 'Dodoma', 'code' => 'DOM001', 'status' => 'active']);
        $branchB = Branch::create(['business_id' => $business->id, 'name' => 'Arusha', 'code' => 'ARU001', 'status' => 'active']);

        $manager = User::factory()->create([
            'employee_number' => 'MGR-001',
            'username' => 'dodoma.manager',
            'status' => 'active',
            'primary_branch_id' => $branchA->id,
        ]);
        $manager->roles()->attach(Role::where('name', 'branch-manager')->firstOrFail());
        $manager->branches()->attach($branchA->id, ['is_primary' => true]);

        $this->actingAs($manager)->get(route('admin.branches.edit', $branchA))->assertOk();
        $this->actingAs($manager)->get(route('admin.branches.edit', $branchB))->assertForbidden();
    }

    public function test_super_admin_can_update_business_settings_and_action_is_audited(): void
    {
        $this->seed(DatabaseSeeder::class);
        $admin = User::where('email', 'admin@microfinance.local')->firstOrFail();

        $this->actingAs($admin)->put(route('admin.business-settings.update'), [
            'name' => 'Sky Microfinance',
            'country' => 'Tanzania',
            'currency' => 'TZS',
            'currency_symbol' => 'TSh',
            'number_format' => '1,234.56',
            'financial_year_start_month' => 1,
            'accounting_period' => 'monthly',
            'default_interest_method' => 'flat',
            'default_penalty_settings' => ['type' => 'fixed', 'value' => '0'],
        ])->assertRedirect();

        $this->assertDatabaseHas('businesses', ['id' => 1, 'name' => 'Sky Microfinance']);
        $this->assertDatabaseHas('audit_logs', ['user_id' => $admin->id, 'action' => 'business.settings.updated']);
    }

    public function test_primary_branch_is_automatically_included_in_staff_assignments(): void
    {
        $this->seed(DatabaseSeeder::class);
        $admin = User::where('email', 'admin@microfinance.local')->firstOrFail();
        $business = Business::firstOrFail();
        $branchA = Branch::create(['business_id' => $business->id, 'name' => 'Dodoma', 'code' => 'DOM002', 'status' => 'active']);
        $branchB = Branch::create(['business_id' => $business->id, 'name' => 'Arusha', 'code' => 'ARU002', 'status' => 'active']);
        $role = Role::where('name', 'loan-officer')->firstOrFail();

        $this->actingAs($admin)->post(route('admin.users.store'), [
            'name' => 'Loan Officer',
            'position' => 'Loan Officer',
            'phone' => '0712111222',
            'email' => 'officer@example.test',
            'username' => 'officer100',
            'primary_branch_id' => $branchB->id,
            'branch_ids' => [$branchA->id],
            'role_ids' => [$role->id],
            'password' => 'Secret@12345',
        ])->assertRedirect(route('admin.users.index'));

        $staff = User::where('username', 'officer100')->firstOrFail();
        $this->assertTrue($staff->branches()->whereKey($branchA->id)->exists());
        $this->assertTrue($staff->branches()->whereKey($branchB->id)->exists());
        $this->assertSame($branchB->id, $staff->primary_branch_id);
    }
}
