<?php

namespace Tests\Feature;

use App\Models\Branch;
use App\Models\Business;
use App\Models\Collateral;
use App\Models\CollateralCategory;
use App\Models\Customer;
use App\Models\Guarantor;
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 Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class LoanApplicationManagementTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_application_creation_generates_number_persists_schedule_and_links_security(): void
    {
        Storage::fake('local');
        [$admin,$branch,$customer,$product,$collateral,$guarantor] = $this->fixture();
        $response=$this->actingAs($admin)->post(route('loans.store'),[
            'branch_id'=>$branch->id,'customer_id'=>$customer->id,'loan_product_id'=>$product->id,
            'principal'=>'500000.00','purpose'=>'Purchase business stock','schedule_start_date'=>now()->format('Y-m-d'),
            'collateral_ids'=>[$collateral->id],'guarantor_id'=>$guarantor->id,
            'supporting_documents'=>[UploadedFile::fake()->create('application.pdf',50,'application/pdf')],
        ]);
        $loan=Loan::where('customer_id',$customer->id)->firstOrFail();
        $response->assertRedirect(route('loans.show',$loan));
        $this->assertMatchesRegularExpression('/^LN-[A-Z0-9]+-'.now()->format('Y').'-000001$/',$loan->loan_number);
        $this->assertSame('draft',$loan->status);
        $this->assertSame('500000.00',$loan->principal);
        $this->assertSame('50000.00',$loan->interest);
        $this->assertSame('6000.00',$loan->fees);
        $this->assertSame('556000.00',$loan->total_repayment);
        $this->assertSame(2,$loan->installments()->count());
        $this->assertSame('556000.00',number_format((float)$loan->installments()->sum('amount_due'),2,'.',''));
        $this->assertTrue($loan->collaterals()->whereKey($collateral->id)->exists());
        $this->assertSame($guarantor->id,$loan->guarantor_id);
        $this->assertDatabaseHas('loan_documents',['loan_id'=>$loan->id,'kind'=>'supporting']);
        $this->assertDatabaseHas('loan_status_histories',['loan_id'=>$loan->id,'to_status'=>'draft']);
        $this->assertDatabaseHas('audit_logs',['action'=>'loan_application.created','auditable_id'=>$loan->id]);
    }

    public function test_required_security_and_product_amount_limits_are_enforced(): void
    {
        [$admin,$branch,$customer,$product] = $this->fixture();
        $this->actingAs($admin)->post(route('loans.store'),[
            'branch_id'=>$branch->id,'customer_id'=>$customer->id,'loan_product_id'=>$product->id,
            'principal'=>'500000.00','purpose'=>'Stock','schedule_start_date'=>now()->format('Y-m-d'),
        ])->assertSessionHasErrors(['collateral_ids']);
        $product->update(['collateral_required'=>false,'guarantor_required'=>false]);
        $this->actingAs($admin)->post(route('loans.store'),[
            'branch_id'=>$branch->id,'customer_id'=>$customer->id,'loan_product_id'=>$product->id,
            'principal'=>'50000.00','purpose'=>'Stock','schedule_start_date'=>now()->format('Y-m-d'),
        ])->assertSessionHasErrors('principal');
        $this->assertSame(0,Loan::count());
    }

    public function test_contract_pdf_signed_upload_and_under_review_transition(): void
    {
        Storage::fake('local');
        [$admin,$branch,$customer,$product,$collateral,$guarantor] = $this->fixture();
        $loan=$this->createThroughHttp($admin,$branch,$customer,$product,$collateral,$guarantor);
        $pdf=$this->actingAs($admin)->post(route('loans.contract',$loan));
        $pdf->assertOk()->assertHeader('content-type','application/pdf');
        $this->assertStringStartsWith('%PDF-1.4',$pdf->getContent());
        $this->assertSame('contract_generated',$loan->fresh()->status);
        $this->assertNotNull($loan->fresh()->contract_generated_at);
        $this->actingAs($admin)->post(route('loans.signed-contract',$loan),[
            'signed_contract'=>UploadedFile::fake()->create('signed.pdf',80,'application/pdf'),
        ])->assertRedirect();
        $this->assertSame('contract_signed',$loan->fresh()->status);
        $this->assertTrue($loan->fresh()->hasSignedContract());
        $this->assertNotNull($loan->fresh()->contract_signed_at);
        $this->actingAs($admin)->post(route('loans.transition',$loan),['status'=>'under_review','notes'=>'Ready for inspector review.'])->assertRedirect();
        $this->assertSame('under_review',$loan->fresh()->status);
        $this->assertDatabaseHas('loan_status_histories',['loan_id'=>$loan->id,'from_status'=>'contract_signed','to_status'=>'under_review']);
    }

    public function test_core_terms_cannot_be_edited_after_submission_and_invalid_transitions_are_rejected(): void
    {
        [$admin,$branch,$customer,$product,$collateral,$guarantor] = $this->fixture();
        $loan=$this->createThroughHttp($admin,$branch,$customer,$product,$collateral,$guarantor);
        $this->actingAs($admin)->post(route('loans.transition',$loan),['status'=>'application_submitted'])->assertRedirect();
        $this->assertSame('application_submitted',$loan->fresh()->status);
        $this->actingAs($admin)->get(route('loans.edit',$loan))->assertStatus(422);
        $this->actingAs($admin)->post(route('loans.transition',$loan),['status'=>'contract_signed'])->assertSessionHasErrors('status');
        $this->assertSame('application_submitted',$loan->fresh()->status);
    }

    public function test_branch_user_cannot_access_another_branch_loan(): void
    {
        [$admin,$branchA,$customer,$product,$collateral,$guarantor] = $this->fixture();
        $loan=$this->createThroughHttp($admin,$branchA,$customer,$product,$collateral,$guarantor);
        $business=Business::firstOrFail();
        $branchB=Branch::create(['business_id'=>$business->id,'name'=>'Arusha','status'=>'active']);
        $user=User::factory()->create(['name'=>'Arusha Loan Officer','username'=>'arusha_loan','status'=>'active','primary_branch_id'=>$branchB->id]);
        $user->roles()->attach(Role::where('name','loan-officer')->firstOrFail());
        $user->branches()->attach($branchB->id,['is_primary'=>true]);
        $this->actingAs($user)->get(route('loans.show',$loan))->assertForbidden();
        $this->actingAs($user)->get(route('loans.index'))->assertDontSee($loan->loan_number);
    }

    public function test_loan_pages_render_standardized_ui_and_business_contract_setting(): void
    {
        [$admin] = $this->fixture();
        $this->actingAs($admin)->get(route('loans.index'))->assertOk()->assertSee('data-ui="table-toolbar"',false);
        $this->actingAs($admin)->get(route('loans.create'))->assertOk()->assertSee('mf-form-section',false)->assertSee('Calculate preview');
        $this->actingAs($admin)->get(route('admin.business-settings.edit'))->assertOk()->assertSee('Require signed contract before disbursement');
    }

    private function fixture(): array
    {
        $admin=User::where('email','admin@microfinance.local')->firstOrFail();
        $business=Business::firstOrFail();
        $branch=Branch::firstOrCreate(['business_id'=>$business->id,'name'=>'Dodoma'],['status'=>'active']);
        $customer=Customer::firstOrCreate(['customer_number'=>'CUS-DOM-LOAN-001'],[
            'branch_id'=>$branch->id,'identity_mode'=>'without_nin','first_name'=>'Amina','last_name'=>'Juma','phone'=>'0755111000','status'=>'active',
        ]);
        $product=LoanProduct::firstOrCreate(['business_id'=>$business->id,'name'=>'Business 2 Month'],[
            'minimum_amount'=>'100000.00','maximum_amount'=>'1000000.00','duration'=>2,'duration_unit'=>'months','interest_rate'=>'10.000000',
            'interest_calculation_method'=>'flat','fixed_interest_amount'=>null,'repayment_frequency'=>'monthly','grace_period_days'=>0,
            'late_payment_penalty_type'=>'fixed','late_payment_penalty_value'=>'5000.000000','processing_fee_type'=>'fixed','processing_fee_value'=>'5000.000000',
            'insurance_fee_type'=>'fixed','insurance_fee_value'=>'0.000000','other_charges'=>'1000.00','collateral_required'=>true,'guarantor_required'=>true,'status'=>'active',
        ]);
        $category=CollateralCategory::where('business_id',$business->id)->firstOrFail();
        $collateral=Collateral::firstOrCreate(['code'=>'COL-LOAN-000001'],[
            'customer_id'=>$customer->id,'branch_id'=>$branch->id,'category_id'=>$category->id,'sequence_number'=>9001,'item'=>'Smart TV',
            'accepted_value'=>'700000.00','received_at'=>now(),'status'=>'accepted',
        ]);
        $guarantor=Guarantor::firstOrCreate(['customer_id'=>$customer->id,'phone'=>'0755222000'],[
            'full_name'=>'Musa Said','nin'=>'19900000000000000000','status'=>'active',
        ]);
        return [$admin,$branch,$customer,$product,$collateral,$guarantor];
    }

    private function createThroughHttp(User $admin, Branch $branch, Customer $customer, LoanProduct $product, Collateral $collateral, Guarantor $guarantor): Loan
    {
        $this->actingAs($admin)->post(route('loans.store'),[
            'branch_id'=>$branch->id,'customer_id'=>$customer->id,'loan_product_id'=>$product->id,'principal'=>'500000.00',
            'purpose'=>'Business expansion','schedule_start_date'=>now()->format('Y-m-d'),'collateral_ids'=>[$collateral->id],'guarantor_id'=>$guarantor->id,
        ])->assertRedirect();
        return Loan::where('customer_id',$customer->id)->latest('id')->firstOrFail();
    }
}
