<?php

namespace Tests\Feature;

use App\Models\Branch;
use App\Models\Business;
use App\Models\Customer;
use App\Models\Loan;
use App\Models\LoanDocument;
use App\Models\LoanProduct;
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 LoanApplicationApprovalLockTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_under_review_locks_contract_generation_signed_replacement_and_supporting_uploads(): void
    {
        [$loan,$admin]=$this->fixture('under_review');
        $loan->update(['contract_generated_at'=>now(),'contract_generated_by'=>$admin->id]);
        $before=LoanDocument::where('loan_id',$loan->id)->count();

        $this->actingAs($admin)->post(route('loans.contract',$loan))->assertStatus(422);
        $this->actingAs($admin)->post(route('loans.signed-contract',$loan),[
            'signed_contract'=>UploadedFile::fake()->create('replacement.pdf',10,'application/pdf'),
        ])->assertStatus(422);
        $this->actingAs($admin)->post(route('loans.documents.store',$loan),[
            'document_type'=>'Extra Review Document',
            'attachment'=>UploadedFile::fake()->image('extra.png'),
        ])->assertStatus(422);

        $this->assertSame($before,LoanDocument::where('loan_id',$loan->id)->count());
        $this->actingAs($admin)->get(route('loans.show',$loan))
            ->assertOk()
            ->assertSee('Contract locked during review.')
            ->assertDontSee('Generate completed contract')
            ->assertDontSee('Upload signed contract')
            ->assertDontSee('Upload supporting document');
    }

    public function test_reviewer_request_states_reopen_contract_and_document_changes(): void
    {
        [$loan,$admin]=$this->fixture('correction_requested');
        $loan->update(['contract_generated_at'=>now(),'contract_generated_by'=>$admin->id]);

        foreach (Loan::REVIEW_CHANGE_REQUEST_STATUSES as $status) {
            $loan->update(['status'=>$status]);
            $loan->refresh();

            $this->assertTrue($loan->canGenerateContract(),$status.' should allow contract regeneration.');
            $this->assertTrue($loan->canUploadSignedContract(),$status.' should allow signed contract upload.');
            $this->assertTrue($loan->canUploadSupportingDocument(),$status.' should allow supporting document upload.');

            $this->actingAs($admin)->post(route('loans.signed-contract',$loan),[
                'signed_contract'=>UploadedFile::fake()->create('signed-'.$status.'.pdf',10,'application/pdf'),
                'notes'=>'Reviewer-requested replacement',
            ])->assertRedirect();

            $this->actingAs($admin)->post(route('loans.documents.store',$loan),[
                'document_type'=>'Requested document',
                'attachment'=>UploadedFile::fake()->image('requested-'.$status.'.png'),
            ])->assertRedirect();

            $this->assertSame($status,$loan->fresh()->status,'Uploading requested files must not silently close the reviewer request.');
        }

        $loan->update(['status'=>'correction_requested']);
        $response=$this->actingAs($admin)->post(route('loans.contract',$loan));
        $response->assertOk()->assertHeader('Content-Type','application/pdf');
        $this->assertSame('correction_requested',$loan->fresh()->status);

        $this->actingAs($admin)->get(route('loans.show',$loan->fresh()))
            ->assertOk()
            ->assertSee('Reviewer changes requested.')
            ->assertSee('Generate completed contract')
            ->assertSee('Upload signed contract')
            ->assertSee('Upload supporting document');
    }

    public function test_rejected_and_approved_applications_are_read_only(): void
    {
        [$loan,$admin]=$this->fixture('rejected');
        $loan->update(['contract_generated_at'=>now(),'contract_generated_by'=>$admin->id]);

        foreach (['rejected','ready_for_disbursement'] as $status) {
            $loan->update(['status'=>$status]);
            $loan->refresh();

            $this->assertFalse($loan->canEditCoreTerms());
            $this->assertFalse($loan->canGenerateContract());
            $this->assertFalse($loan->canUploadSignedContract());
            $this->assertFalse($loan->canUploadSupportingDocument());
            $this->assertTrue($loan->isApplicationApprovalLocked());

            $this->actingAs($admin)->get(route('loans.edit',$loan))->assertStatus(422);
            $this->actingAs($admin)->post(route('loans.contract',$loan))->assertStatus(422);
            $this->actingAs($admin)->post(route('loans.signed-contract',$loan),[
                'signed_contract'=>UploadedFile::fake()->create('locked.pdf',10,'application/pdf'),
            ])->assertStatus(422);
            $this->actingAs($admin)->post(route('loans.documents.store',$loan),[
                'document_type'=>'Locked document',
                'attachment'=>UploadedFile::fake()->image('locked.png'),
            ])->assertStatus(422);

            $this->actingAs($admin)->get(route('loans.show',$loan))
                ->assertOk()
                ->assertSee('Application locked.')
                ->assertDontSee('Generate completed contract')
                ->assertDontSee('Upload signed contract')
                ->assertDontSee('Upload supporting document');
        }
    }

    /** @return array{0:Loan,1:User} */
    private function fixture(string $status): array
    {
        $business=Business::firstOrFail();
        $branch=Branch::create([
            'business_id'=>$business->id,
            'name'=>'Approval Lock Branch '.str($status)->headline(),
            'location'=>'Dodoma',
            'status'=>'active',
        ]);
        $admin=User::where('username','admin')->firstOrFail();
        $customer=Customer::create([
            'branch_id'=>$branch->id,
            'customer_number'=>'CUS-LOCK-'.strtoupper(substr(md5($status),0,8)),
            'identity_mode'=>'with_nin',
            'first_name'=>'Asha',
            'last_name'=>'Lock',
            'nin'=>'19900101'.str_pad((string)Customer::count(),12,'0',STR_PAD_LEFT),
            'phone'=>'071200'.str_pad((string)Customer::count(),4,'0',STR_PAD_LEFT),
            'status'=>'active',
            'created_by'=>$admin->id,
        ]);
        $product=LoanProduct::create([
            'business_id'=>$business->id,
            'name'=>'Approval Lock Product',
            'minimum_amount'=>'1000.00',
            'maximum_amount'=>'5000000.00',
            'duration'=>1,
            'duration_unit'=>'months',
            'interest_rate'=>'10.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',
            'processing_fee_payment_mode'=>'financed',
            'insurance_fee_type'=>'fixed',
            'insurance_fee_value'=>'0.000000',
            'other_charges'=>'0.00',
            'collateral_required'=>false,
            'guarantor_required'=>false,
            'status'=>'active',
        ]);
        $loan=Loan::create([
            'branch_id'=>$branch->id,
            'customer_id'=>$customer->id,
            'loan_product_id'=>$product->id,
            'sequence_number'=>1,
            'sequence_year'=>2026,
            'loan_number'=>'LN-LOCK-'.strtoupper(substr(md5($status),0,8)),
            'principal'=>'100000.00',
            'interest'=>'10000.00',
            'processing_fee'=>'0.00',
            'processing_fee_payment_mode'=>'financed',
            'insurance_fee'=>'0.00',
            'other_charges'=>'0.00',
            'fees'=>'0.00',
            'total_repayment'=>'110000.00',
            'purpose'=>'Approval locking workflow test',
            'schedule_start_date'=>'2026-08-15',
            'status'=>$status,
            'created_by'=>$admin->id,
            'updated_by'=>$admin->id,
        ]);

        return [$loan,$admin];
    }
}
