<?php

namespace Tests\Feature\CreditScore;

use App\Models\Branch;
use App\Models\Business;
use App\Models\Customer;
use App\Models\CustomerRiskProfile;
use App\Models\Loan;
use App\Models\LoanProduct;
use App\Models\User;
use App\Services\CreditScore\CreditScorePortfolioReportService;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class CreditScoreProductionHardeningTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_stale_refresh_updates_missing_and_stale_profiles_without_history_noise(): void
    {
        $business = Business::query()->firstOrFail();
        $branchA = Branch::factory()->create(['business_id' => $business->id]);
        $branchB = Branch::factory()->create(['business_id' => $business->id]);

        $missing = $this->customer($branchA);
        $stale = $this->customer($branchA);
        $fresh = $this->customer($branchA);
        $otherBranch = $this->customer($branchB);

        $this->profile($stale, [
            'calculated_at' => now()->subDays(2),
            'last_trigger' => 'old_fixture',
        ]);
        $this->profile($fresh, [
            'calculated_at' => now(),
            'last_trigger' => 'fresh_fixture',
        ]);

        $historyCount = DB::table('customer_credit_score_histories')->count();

        $this->artisan('credit-scores:refresh-stale', [
            '--branch' => $branchA->id,
            '--hours' => 24,
            '--chunk' => 1,
        ])->assertSuccessful();

        $this->assertSame(
            'stale_daily_refresh',
            CustomerRiskProfile::withoutGlobalScopes()->where('customer_id', $missing->id)->value('last_trigger'),
        );
        $this->assertSame(
            'stale_daily_refresh',
            CustomerRiskProfile::withoutGlobalScopes()->where('customer_id', $stale->id)->value('last_trigger'),
        );
        $this->assertSame(
            'fresh_fixture',
            CustomerRiskProfile::withoutGlobalScopes()->where('customer_id', $fresh->id)->value('last_trigger'),
        );
        $this->assertFalse(
            CustomerRiskProfile::withoutGlobalScopes()->where('customer_id', $otherBranch->id)->exists(),
        );
        $this->assertSame($historyCount, DB::table('customer_credit_score_histories')->count());
    }

    public function test_portfolio_report_uses_only_selected_branches_and_current_active_exposure(): void
    {
        $business = Business::query()->firstOrFail();
        $branchA = Branch::factory()->create(['business_id' => $business->id]);
        $branchB = Branch::factory()->create(['business_id' => $business->id]);
        $customerA = $this->customer($branchA);
        $customerB = $this->customer($branchB);

        $profileA = $this->profile($customerA, [
            'score' => 700,
            'score_band' => 'good',
            'risk_level' => 'moderate',
            'confidence' => 80,
        ]);
        $this->profile($customerB, [
            'score' => 520,
            'score_band' => 'poor',
            'risk_level' => 'very_high',
            'confidence' => 70,
        ]);

        $product = LoanProduct::factory()->create(['business_id' => $business->id]);
        Loan::factory()->create([
            'branch_id' => $branchA->id,
            'customer_id' => $customerA->id,
            'loan_product_id' => $product->id,
            'status' => 'active',
            'principal' => 600000,
            'principal_outstanding' => 500000,
            'total_outstanding' => 500000,
            'total_overdue' => 50000,
        ]);
        Loan::factory()->create([
            'branch_id' => $branchB->id,
            'customer_id' => $customerB->id,
            'loan_product_id' => $product->id,
            'status' => 'active',
            'principal' => 900000,
            'principal_outstanding' => 800000,
            'total_outstanding' => 800000,
            'total_overdue' => 400000,
        ]);

        $historyCount = DB::table('customer_credit_score_histories')->count();
        $profileUpdatedAt = $profileA->updated_at?->copy();

        $data = app(CreditScorePortfolioReportService::class)->generate([$branchA->id]);

        $this->assertSame(1, $data['summary']['total_customers']);
        $this->assertSame(1, $data['summary']['scored_customers']);
        $this->assertSame(700.0, $data['summary']['average_score']);
        $this->assertSame(500000.0, $data['summary']['total_outstanding']);
        $this->assertSame(50000.0, $data['summary']['total_overdue']);
        $this->assertSame(10.0, $data['summary']['arrears_rate']);

        $good = collect($data['bands'])->firstWhere('key', 'good');
        $this->assertNotNull($good);
        $this->assertSame(1, $good['customers']);
        $this->assertSame(1, $good['active_loans']);
        $this->assertSame(500000.0, $good['outstanding']);
        $this->assertSame(50000.0, $good['overdue']);

        $this->assertCount(1, $data['branches']);
        $this->assertSame($branchA->id, $data['branches'][0]['branch_id']);
        $this->assertSame($historyCount, DB::table('customer_credit_score_histories')->count());
        $this->assertEquals(
            $profileUpdatedAt,
            CustomerRiskProfile::withoutGlobalScopes()->findOrFail($profileA->id)->updated_at,
        );
    }

    public function test_portfolio_route_rejects_branch_outside_user_scope(): void
    {
        $business = Business::query()->firstOrFail();
        $branchA = Branch::factory()->create(['business_id' => $business->id]);
        $branchB = Branch::factory()->create(['business_id' => $business->id]);
        $this->customer($branchA);
        $this->customer($branchB);

        $user = User::factory()->create(['primary_branch_id' => $branchA->id]);
        $user->branches()->sync([$branchA->id]);
        $user->assignRole('branch-manager');

        $this->actingAs($user);

        $this->get(route('credit-score-reports.portfolio', [
            'branch_ids' => [$branchB->id],
        ]))->assertForbidden();
    }

    public function test_portfolio_route_defaults_to_accessible_branches_only(): void
    {
        $business = Business::query()->firstOrFail();
        $branchA = Branch::factory()->create([
            'business_id' => $business->id,
            'name' => 'Allowed Credit Branch',
        ]);
        $branchB = Branch::factory()->create([
            'business_id' => $business->id,
            'name' => 'Hidden Credit Branch',
        ]);
        $this->customer($branchA);
        $this->customer($branchB);

        $user = User::factory()->create(['primary_branch_id' => $branchA->id]);
        $user->branches()->sync([$branchA->id]);
        $user->assignRole('branch-manager');

        $this->actingAs($user);

        $this->get(route('credit-score-reports.portfolio'))
            ->assertOk()
            ->assertSee('Allowed Credit Branch')
            ->assertDontSee('Hidden Credit Branch');
    }

    private function customer(Branch $branch): Customer
    {
        return Customer::factory()->create([
            'branch_id' => $branch->id,
            'monthly_income' => 500000,
            'occupation' => 'Trader',
        ]);
    }

    private function profile(Customer $customer, array $overrides = []): CustomerRiskProfile
    {
        return CustomerRiskProfile::withoutGlobalScopes()->create(array_merge([
            'customer_id' => $customer->id,
            'branch_id' => $customer->branch_id,
            'score' => 700,
            'risk_level' => 'moderate',
            'score_band' => 'good',
            'score_version' => (string) config('credit_score.version', 'v1'),
            'confidence' => 80,
            'thin_file' => false,
            'manual_review_required' => false,
            'calculated_at' => now(),
            'last_trigger' => 'test_fixture',
        ], $overrides));
    }
}
