<?php

namespace Tests\Feature\BranchIsolation;

use App\Models\Branch;
use App\Models\Business;
use App\Models\Customer;
use App\Models\FinancialAccount;
use App\Models\Loan;
use App\Models\LoanEarlySettlement;
use App\Models\LoanProduct;
use App\Models\LoanTopUp;
use App\Models\LoanWriteOff;
use App\Models\LoanWriteOffRecovery;
use App\Models\Role;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LoanAdjustmentAccountIntegrityTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_multi_branch_accountant_cannot_mix_accounts_across_top_up_settlement_or_write_off_recovery(): void
    {
        [$branchA, $branchB, $accountA, $accountB, $actor] = $this->branchFixture();
        [$activeLoan, $writtenOffLoan, $writeOff] = $this->loanFixture($branchA, $actor);
        $balanceA = $accountA->current_balance;
        $balanceB = $accountB->current_balance;

        $this->actingAs($actor)
            ->from(route('loan-top-ups.index'))
            ->post(route('loan-top-ups.execute', $activeLoan), [
                'financial_account_id' => $accountB->id,
                'date' => '2026-08-13',
                'reference' => 'TOP-CROSS-BRANCH',
            ])
            ->assertRedirect(route('loan-top-ups.index'))
            ->assertSessionHasErrors('financial_account_id');

        $this->assertSame(0, LoanTopUp::count());
        $this->assertSame('active', $activeLoan->fresh()->status);
        $this->assertSame($balanceA, $accountA->fresh()->current_balance);
        $this->assertSame($balanceB, $accountB->fresh()->current_balance);

        $this->actingAs($actor)
            ->from(route('loan-adjustments.index'))
            ->post(route('loan-early-settlements.store', $activeLoan), [
                'financial_account_id' => $accountB->id,
                'date' => '2026-08-13',
                'settlement_adjustment' => '0.00',
                'reference' => 'STL-CROSS-BRANCH',
            ])
            ->assertRedirect(route('loan-adjustments.index'))
            ->assertSessionHasErrors('financial_account_id');

        $this->assertSame(0, LoanEarlySettlement::count());
        $this->assertSame('active', $activeLoan->fresh()->status);
        $this->assertSame('10000.00', $activeLoan->fresh()->total_outstanding);
        $this->assertSame($balanceA, $accountA->fresh()->current_balance);
        $this->assertSame($balanceB, $accountB->fresh()->current_balance);

        $this->actingAs($actor)
            ->from(route('loan-write-offs.show', $writeOff))
            ->post(route('loan-write-offs.recoveries.store', $writeOff), [
                'financial_account_id' => $accountB->id,
                'amount' => '1000.00',
                'date' => '2026-08-13',
                'method' => 'cash',
                'reference' => 'WRC-CROSS-BRANCH',
            ])
            ->assertRedirect(route('loan-write-offs.show', $writeOff))
            ->assertSessionHasErrors('financial_account_id');

        $this->assertSame(0, LoanWriteOffRecovery::count());
        $this->assertSame('written_off', $writtenOffLoan->fresh()->status);
        $this->assertSame('0.00', $writeOff->fresh()->recovered_amount);
        $this->assertSame('10000.00', $writeOff->fresh()->recoverable_balance);
        $this->assertSame($balanceA, $accountA->fresh()->current_balance);
        $this->assertSame($balanceB, $accountB->fresh()->current_balance);
    }

    private function branchFixture(): array
    {
        $business = Business::firstOrFail();
        $branchA = Branch::create([
            'business_id' => $business->id,
            'name' => 'Loan Integrity A',
            'status' => 'active',
        ]);
        $branchB = Branch::create([
            'business_id' => $business->id,
            'name' => 'Loan Integrity B',
            'status' => 'active',
        ]);
        $accountA = FinancialAccount::create([
            'branch_id' => $branchA->id,
            'name' => 'Loan Integrity Cash A',
            'account_type' => 'cash',
            'opening_balance' => '20000.00',
            'current_balance' => '20000.00',
            'status' => 'active',
        ]);
        $accountB = FinancialAccount::create([
            'branch_id' => $branchB->id,
            'name' => 'Loan Integrity Cash B',
            'account_type' => 'cash',
            'opening_balance' => '20000.00',
            'current_balance' => '20000.00',
            'status' => 'active',
        ]);
        $actor = User::factory()->create([
            'name' => 'Multi Branch Loan Integrity Accountant',
            'username' => 'multi_branch_loan_integrity',
            'status' => 'active',
            'primary_branch_id' => $branchA->id,
        ]);
        $actor->roles()->attach(Role::where('name', 'accountant')->firstOrFail());
        $actor->branches()->attach($branchA->id, ['is_primary' => true]);
        $actor->branches()->attach($branchB->id, ['is_primary' => false]);

        return [$branchA, $branchB, $accountA, $accountB, $actor];
    }

    private function loanFixture(Branch $branch, User $actor): array
    {
        $business = $branch->business;
        $customer = Customer::create([
            'branch_id' => $branch->id,
            'customer_number' => 'CUS-LOAN-INTEGRITY',
            'identity_mode' => 'without_nin',
            'first_name' => 'Neema',
            'last_name' => 'Musa',
            'phone' => '0755007722',
            'status' => 'active',
        ]);
        $product = LoanProduct::create([
            'business_id' => $business->id,
            'name' => 'Loan Integrity Product',
            'minimum_amount' => '1000.00',
            'maximum_amount' => '500000.00',
            'duration' => 1,
            'duration_unit' => 'months',
            'interest_rate' => '0.000000',
            'interest_calculation_method' => 'flat',
            'repayment_frequency' => 'monthly',
            'grace_period_days' => 0,
            'late_payment_penalty_type' => 'none',
            'late_payment_penalty_value' => '0.000000',
            'processing_fee_type' => 'fixed',
            'processing_fee_value' => '0.000000',
            'insurance_fee_type' => 'fixed',
            'insurance_fee_value' => '0.000000',
            'other_charges' => '0.00',
            'collateral_required' => false,
            'guarantor_required' => false,
            'status' => 'active',
        ]);

        $activeLoan = $this->loan($branch, $customer, $product, 'LN-ADJ-INTEGRITY-A', 'active');
        $writtenOffLoan = $this->loan($branch, $customer, $product, 'LN-ADJ-INTEGRITY-W', 'written_off');
        $writtenOffLoan->update([
            'written_off_at' => now(),
            'written_off_by' => $actor->id,
        ]);

        $writeOff = LoanWriteOff::create([
            'write_off_number' => 'WOF-INTEGRITY-2026-000001',
            'loan_id' => $writtenOffLoan->id,
            'customer_id' => $customer->id,
            'branch_id' => $branch->id,
            'original_principal' => '10000.00',
            'original_total_repayment' => '10000.00',
            'principal_outstanding' => '10000.00',
            'interest_outstanding' => '0.00',
            'fees_outstanding' => '0.00',
            'penalties_outstanding' => '0.00',
            'write_off_amount' => '10000.00',
            'recovered_amount' => '0.00',
            'recoverable_balance' => '10000.00',
            'reason' => 'Integrity regression fixture',
            'status' => 'approved',
            'requested_by' => $actor->id,
            'requested_at' => now(),
            'approved_by' => $actor->id,
            'approved_at' => now(),
        ]);

        return [$activeLoan, $writtenOffLoan, $writeOff];
    }

    private function loan(Branch $branch, Customer $customer, LoanProduct $product, string $number, string $status): Loan
    {
        return Loan::create([
            'branch_id' => $branch->id,
            'customer_id' => $customer->id,
            'loan_product_id' => $product->id,
            'sequence_number' => str_ends_with($number, '-W') ? 2 : 1,
            'sequence_year' => 2026,
            'loan_number' => $number,
            'principal' => '10000.00',
            'interest' => '0.00',
            'processing_fee' => '0.00',
            'insurance_fee' => '0.00',
            'other_charges' => '0.00',
            'fees' => '0.00',
            'total_repayment' => '10000.00',
            'principal_outstanding' => '10000.00',
            'interest_outstanding' => '0.00',
            'fees_outstanding' => '0.00',
            'penalties_outstanding' => '0.00',
            'total_outstanding' => '10000.00',
            'total_paid' => '0.00',
            'purpose' => 'Integrity validation',
            'schedule_start_date' => '2026-08-13',
            'status' => $status,
            'disbursed_at' => now(),
            'activated_at' => now(),
            'schedule_activated_at' => now(),
        ]);
    }
}
