<?php

namespace Tests\Feature\CreditScore;

use App\Models\Branch;
use App\Models\Business;
use App\Models\Customer;
use App\Models\CustomerCreditScoreHistory;
use App\Models\Loan;
use App\Models\LoanProduct;
use App\Models\Role;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class CreditScoreUiTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_credit_score_permissions_are_limited_to_lending_roles(): void
    {
        [, $branch, $customer] = $this->fixture();
        $officer = $this->staff('loan-officer', $branch, 'score_ui_officer');
        $inspector = $this->staff('loan-inspector', $branch, 'score_ui_inspector');
        $cashier = $this->staff('cashier', $branch, 'score_ui_cashier');

        foreach ([$officer, $inspector] as $user) {
            $this->assertTrue($user->can('credit-score.view'));
            $this->assertTrue($user->can('credit-score.history'));
            $this->assertTrue($user->can('credit-score.recalculate'));
        }

        $this->assertTrue($cashier->can('customers.view'));
        $this->assertFalse($cashier->can('credit-score.view'));
        $this->assertFalse($cashier->can('credit-score.history'));
        $this->assertFalse($cashier->can('credit-score.recalculate'));

        $this->actingAs($cashier)
            ->get(route('customers.show', $customer))
            ->assertOk()
            ->assertDontSee('Credit score');
    }

    public function test_customer_profile_and_history_render_explainable_score_for_authorized_staff(): void
    {
        [, $branch, $customer] = $this->fixture();
        $officer = $this->staff('loan-officer', $branch, 'score_profile_officer');
        $this->storedScore($customer, 724, true);
        $this->history($customer, $branch, 710, 'loan_payment_recorded');
        $this->history($customer, $branch, 724, 'arrears_changed');

        $this->actingAs($officer)
            ->get(route('customers.show', $customer))
            ->assertOk()
            ->assertSee('Credit score')
            ->assertSee('724')
            ->assertSee('Good')
            ->assertSee('Manual review')
            ->assertSee('Most installments were paid on time.')
            ->assertSee('Debt service requires reviewer attention.')
            ->assertSee('Recent score trend')
            ->assertSee('Recent loans');

        $this->actingAs($officer)
            ->get(route('customers.credit-score.history', $customer))
            ->assertOk()
            ->assertSee('Credit score history')
            ->assertSee('Score snapshots')
            ->assertSee('Loan Payment Recorded')
            ->assertSee('Arrears Changed');
    }

    public function test_manual_recalculation_is_permissioned_audited_and_branch_scoped(): void
    {
        [$business, $branchA, $customerA] = $this->fixture();
        $branchB = Branch::create(['business_id' => $business->id, 'name' => 'Mwanza Credit UI', 'status' => 'active']);
        $officerA = $this->staff('loan-officer', $branchA, 'score_recalc_a');
        $officerB = $this->staff('loan-officer', $branchB, 'score_recalc_b');
        $cashierA = $this->staff('cashier', $branchA, 'score_recalc_cashier');

        $this->actingAs($cashierA)
            ->post(route('customers.credit-score.recalculate', $customerA))
            ->assertForbidden();

        $this->actingAs($officerA)
            ->post(route('customers.credit-score.recalculate', $customerA))
            ->assertRedirect();

        $this->assertDatabaseHas('customer_credit_score_histories', [
            'customer_id' => $customerA->id,
            'branch_id' => $branchA->id,
            'trigger' => 'manual_recalculation',
        ]);
        $this->assertDatabaseHas('audit_logs', [
            'action' => 'credit_score.recalculated',
            'auditable_id' => $customerA->id,
        ]);

        $crossBranch = $this->actingAs($officerB)
            ->get(route('customers.credit-score.history', $customerA));
        $this->assertContains($crossBranch->status(), [403, 404]);
    }

    public function test_loan_review_shows_immutable_application_score_separately_from_new_current_score(): void
    {
        [$business, $branch, $customer, $product] = $this->fixture();
        $business->update(['loan_approval_mode' => 'approval_required']);
        $officer = $this->staff('loan-officer', $branch, 'score_loan_officer');
        $inspector = $this->staff('loan-inspector', $branch, 'score_loan_inspector');

        $loan = $this->createLoan($officer, $branch, $customer, $product);
        $this->actingAs($officer)->post(route('loans.transition', $loan), [
            'status' => 'application_submitted',
        ])->assertRedirect();
        $this->actingAs($officer)->post(route('loans.transition', $loan), [
            'status' => 'under_review',
            'notes' => 'Submitted for score UI review.',
        ])->assertRedirect();

        $loan->refresh();
        $snapshotId = $loan->application_credit_score_history_id;
        $this->assertNotNull($snapshotId);

        $customer->update(['monthly_income' => '100000.00']);
        $this->actingAs($officer)
            ->post(route('customers.credit-score.recalculate', $customer))
            ->assertRedirect();

        $this->assertSame($snapshotId, $loan->fresh()->application_credit_score_history_id);

        $this->actingAs($inspector)
            ->get(route('loans.show', $loan))
            ->assertOk()
            ->assertSee('Credit review')
            ->assertSee('Application score')
            ->assertSee('Current score')
            ->assertSee('Decision support only')
            ->assertSee('Debt service ratio')
            ->assertSee('Collateral coverage')
            ->assertSee('Current arrears')
            ->assertSee('does not approve or reject this application');
    }

    private function fixture(): array
    {
        $business = Business::firstOrFail();
        $branch = Branch::firstOrCreate(
            ['business_id' => $business->id, 'name' => 'Dodoma Credit UI'],
            ['status' => 'active'],
        );
        $customer = Customer::firstOrCreate(
            ['customer_number' => 'CUS-CREDIT-UI-001'],
            [
                'branch_id' => $branch->id,
                'identity_mode' => 'without_nin',
                'first_name' => 'Asha',
                'last_name' => 'Mollel',
                'phone' => '0755666000',
                'monthly_income' => '1000000.00',
                'occupation' => 'Retail trader',
                'status' => 'active',
            ],
        );
        $product = LoanProduct::firstOrCreate(
            ['business_id' => $business->id, 'name' => 'Credit UI Product'],
            [
                'minimum_amount' => '100000.00',
                'maximum_amount' => '1000000.00',
                'duration' => 2,
                'duration_unit' => 'months',
                'interest_rate' => '10.000000',
                'interest_calculation_method' => 'flat',
                'repayment_frequency' => 'monthly',
                'grace_period_days' => 0,
                'late_payment_penalty_type' => 'fixed',
                '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',
            ],
        );

        return [$business, $branch, $customer, $product];
    }

    private function staff(string $role, Branch $branch, string $username): User
    {
        $user = User::factory()->create([
            'name' => str($username)->replace('_', ' ')->title()->toString(),
            'username' => $username,
            'status' => 'active',
            'primary_branch_id' => $branch->id,
        ]);
        $user->roles()->attach(Role::where('name', $role)->firstOrFail());
        $user->branches()->attach($branch->id, ['is_primary' => true]);

        return $user;
    }

    private function storedScore(Customer $customer, int $score, bool $manualReview): void
    {
        $customer->riskProfile()->create([
            'risk_level' => 'moderate',
            'score' => $score,
            'score_band' => 'good',
            'score_version' => 'v1',
            'confidence' => '82.00',
            'thin_file' => false,
            'manual_review_required' => $manualReview,
            'last_trigger' => 'test_fixture',
            'factors' => [
                'band_label' => 'Good',
                'components' => [
                    'repayment_behaviour' => [
                        'positive_factors' => ['Most installments were paid on time.'],
                        'negative_factors' => [],
                        'factors' => ['on_time_rate' => 0.92],
                    ],
                    'affordability' => [
                        'positive_factors' => [],
                        'negative_factors' => ['Debt service requires reviewer attention.'],
                        'factors' => ['debt_service_ratio' => 0.31],
                    ],
                    'collateral' => [
                        'positive_factors' => [],
                        'negative_factors' => [],
                        'factors' => ['required' => false],
                    ],
                ],
                'overlays' => [],
            ],
            'explanation' => 'Good credit profile with repayment strengths and affordability factors to review.',
            'calculated_at' => now(),
        ]);
    }

    private function history(Customer $customer, Branch $branch, int $score, string $trigger): CustomerCreditScoreHistory
    {
        return CustomerCreditScoreHistory::create([
            'customer_id' => $customer->id,
            'branch_id' => $branch->id,
            'score' => $score,
            'score_band' => 'good',
            'risk_level' => 'moderate',
            'confidence' => '80.00',
            'thin_file' => false,
            'manual_review_required' => false,
            'score_version' => 'v1',
            'trigger' => $trigger,
            'component_scores' => ['repayment_behaviour' => ['normalized_score' => 80, 'available' => true]],
            'factors' => ['band_label' => 'Good', 'components' => []],
            'explanation' => 'Score history UI fixture.',
            'calculated_at' => now(),
        ]);
    }

    private function createLoan(User $creator, Branch $branch, Customer $customer, LoanProduct $product): Loan
    {
        $this->actingAs($creator)->post(route('loans.store'), [
            'branch_id' => $branch->id,
            'customer_id' => $customer->id,
            'loan_product_id' => $product->id,
            'principal' => '500000.00',
            'purpose' => 'Working capital',
            'schedule_start_date' => now()->format('Y-m-d'),
        ])->assertRedirect();

        return Loan::query()->where('customer_id', $customer->id)->latest('id')->firstOrFail();
    }
}
