<?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 App\Services\CustomerRiskService;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class CreditScoreWorkflowIntegrationTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_submission_links_immutable_proposed_loan_snapshot_without_overwriting_current_score(): void
    {
        [$business, $branch, $customer, $product] = $this->fixture();
        $business->update(['loan_approval_mode' => 'approval_required']);
        $customer->update([
            'monthly_income' => '1000000.00',
            'occupation' => 'Retail trader',
        ]);
        $officer = $this->staff('loan-officer', $branch, 'score_snapshot_officer');
        $loan = $this->createLoan($officer, $branch, $customer, $product);

        $this->actingAs($officer);
        $baseline = app(CustomerRiskService::class)->refresh($customer->fresh(), 'baseline_before_application');
        $baselineScore = (float) $baseline->score;
        $baselineCalculatedAt = $baseline->calculated_at?->format('Y-m-d H:i:s.u');

        $this->submitForReview($officer, $loan);

        $loan->refresh();
        $this->assertSame('under_review', $loan->status);
        $this->assertNotNull($loan->application_credit_score_history_id);

        $snapshot = $loan->applicationCreditScoreHistory()->firstOrFail();
        $this->assertSame('loan_application_submitted', $snapshot->trigger);
        $this->assertSame($customer->id, $snapshot->customer_id);
        $this->assertSame($branch->id, $snapshot->branch_id);
        $this->assertSame('v1', $snapshot->score_version);
        $this->assertArrayHasKey('affordability', $snapshot->component_scores);
        $this->assertGreaterThan(
            0,
            (float) data_get($snapshot->factors, 'components.affordability.factors.proposed_monthly_installment', 0),
        );

        $current = $customer->riskProfile()->firstOrFail();
        $this->assertSame('baseline_before_application', $current->last_trigger);
        $this->assertSame($baselineScore, (float) $current->score);
        $this->assertSame($baselineCalculatedAt, $current->calculated_at?->format('Y-m-d H:i:s.u'));

        $snapshotId = $snapshot->id;
        $snapshotScore = (float) $snapshot->score;
        $snapshotFactors = $snapshot->factors;

        $customer->update(['monthly_income' => '250000.00']);
        app(CustomerRiskService::class)->refresh($customer->fresh(), 'customer_income_updated');

        $this->assertSame($snapshotId, $loan->fresh()->application_credit_score_history_id);
        $unchangedSnapshot = CustomerCreditScoreHistory::query()->findOrFail($snapshotId);
        $this->assertSame($snapshotScore, (float) $unchangedSnapshot->score);
        $this->assertSame($snapshotFactors, $unchangedSnapshot->factors);
        $this->assertSame('customer_income_updated', $customer->riskProfile()->firstOrFail()->last_trigger);
    }

    public function test_manual_review_score_does_not_automatically_block_authorized_auto_approval(): void
    {
        [$business, $branch, $customer, $product] = $this->fixture();
        $business->update([
            'loan_approval_mode' => 'auto_authorized',
            'maker_checker_enabled' => false,
            'manager_approval_threshold' => null,
        ]);
        $customer->update([
            'occupation' => null,
            'employer_business' => null,
        ]);

        $manager = $this->staff('branch-manager', $branch, 'score_auto_manager');
        $loan = $this->createLoan($manager, $branch, $customer, $product);

        $this->actingAs($manager)->post(route('loans.transition', $loan), [
            'status' => 'application_submitted',
        ])->assertRedirect();

        DB::table('customers')->where('id', $customer->id)->update([
            'monthly_income' => '0.00',
            'occupation' => null,
            'employer_business' => null,
            'updated_at' => now(),
        ]);
        $this->assertSame(0.0, (float) DB::table('customers')->where('id', $customer->id)->value('monthly_income'));

        $this->actingAs($manager)->post(route('loans.transition', $loan), [
            'status' => 'under_review',
            'notes' => 'Authorized auto approval submission.',
        ])->assertRedirect();

        $loan->refresh();
        $this->assertSame('ready_for_disbursement', $loan->status);
        $this->assertNotNull($loan->application_credit_score_history_id);

        $snapshot = $loan->applicationCreditScoreHistory()->firstOrFail();
        $this->assertTrue(
            $snapshot->manual_review_required,
            'Expected missing-income overlay at application snapshot time. Factors: '.json_encode($snapshot->factors),
        );
        $this->assertContains(
            'missing_income_for_affordability',
            collect($snapshot->factors['overlays'] ?? [])->pluck('key')->all(),
        );

        $this->assertDatabaseHas('loan_approval_decisions', [
            'loan_id' => $loan->id,
            'decision' => 'auto_approve',
            'new_status' => 'ready_for_disbursement',
            'user_id' => $manager->id,
            'is_auto' => true,
        ]);
    }

    private function fixture(): array
    {
        $business = Business::firstOrFail();
        $branch = Branch::firstOrCreate(
            ['business_id' => $business->id, 'name' => 'Dodoma Credit Score'],
            ['status' => 'active'],
        );
        $customer = Customer::firstOrCreate(
            ['customer_number' => 'CUS-CREDIT-WORKFLOW-001'],
            [
                'branch_id' => $branch->id,
                'identity_mode' => 'without_nin',
                'first_name' => 'Asha',
                'last_name' => 'Juma',
                'phone' => '0755444000',
                'monthly_income' => '1000000.00',
                'occupation' => 'Retail trader',
                'status' => 'active',
            ],
        );
        $product = LoanProduct::firstOrCreate(
            ['business_id' => $business->id, 'name' => 'Credit Score 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 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();
    }

    private function submitForReview(User $user, Loan $loan): void
    {
        $this->actingAs($user)->post(route('loans.transition', $loan), [
            'status' => 'application_submitted',
        ])->assertRedirect();
        $this->actingAs($user)->post(route('loans.transition', $loan), [
            'status' => 'under_review',
            'notes' => 'Submitted for approval.',
        ])->assertRedirect();
    }
}
