<?php

namespace Tests\Unit;

use App\Models\Business;
use App\Models\LoanProduct;
use App\Services\LoanCalculationService;
use App\Services\MoneyMath;
use App\Services\RepaymentScheduleGenerator;
use Carbon\CarbonImmutable;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LoanCalculationServiceTest extends TestCase
{
    use RefreshDatabase;

    private function product(array $overrides=[]): LoanProduct
    {
        $business=Business::factory()->create();
        return LoanProduct::factory()->create(array_merge(['business_id'=>$business->id,'minimum_amount'=>'1.00','maximum_amount'=>'10000000.00'],$overrides));
    }

    public function test_flat_interest_calculation(): void
    {
        $p=$this->product(['interest_calculation_method'=>'flat','interest_rate'=>'10.000000','duration'=>1,'duration_unit'=>'months','repayment_frequency'=>'monthly']);
        $r=app(LoanCalculationService::class)->calculate($p,'100000.00',CarbonImmutable::parse('2026-08-01'));
        $this->assertSame('100000.00',$r['principal']); $this->assertSame('10000.00',$r['interest']); $this->assertSame('110000.00',$r['total_repayment']); $this->assertSame(1,$r['installment_count']);
    }

    public function test_fixed_interest_amount(): void
    {
        $p=$this->product(['interest_calculation_method'=>'fixed_amount','interest_rate'=>'0','fixed_interest_amount'=>'12000.00']);
        $r=app(LoanCalculationService::class)->calculate($p,'100000.00',CarbonImmutable::parse('2026-08-01'));
        $this->assertSame('12000.00',$r['interest']); $this->assertSame('112000.00',$r['total_repayment']);
    }

    public function test_reducing_balance_uses_outstanding_principal_each_period(): void
    {
        $p=$this->product(['interest_calculation_method'=>'reducing_balance','interest_rate'=>'20.000000','duration'=>2,'duration_unit'=>'months','repayment_frequency'=>'monthly']);
        $r=app(RepaymentScheduleGenerator::class)->generate($p,'100000.00',CarbonImmutable::parse('2026-08-01'));
        $this->assertSame(2,$r['summary']['installment_count']); $this->assertSame('15000.00',$r['summary']['interest']); $this->assertSame('10000.00',$r['installments'][0]['interest']); $this->assertSame('5000.00',$r['installments'][1]['interest']); $this->assertSame('115000.00',$r['summary']['total_repayment']);
    }

    public function test_fixed_and_percentage_fees_are_separated(): void
    {
        $p=$this->product(['interest_rate'=>'10.000000','processing_fee_type'=>'percentage','processing_fee_value'=>'2.000000','insurance_fee_type'=>'fixed','insurance_fee_value'=>'1000.000000','other_charges'=>'500.00']);
        $r=app(LoanCalculationService::class)->calculate($p,'100000.00',CarbonImmutable::parse('2026-08-01'));
        $this->assertSame('2000.00',$r['processing_fee']); $this->assertSame('1000.00',$r['insurance_fee']); $this->assertSame('500.00',$r['other_charges']); $this->assertSame('3500.00',$r['fees']); $this->assertSame('113500.00',$r['total_repayment']);
    }

    public function test_weekly_schedule_uses_term_and_frequency(): void
    {
        $p=$this->product(['duration'=>4,'duration_unit'=>'weeks','repayment_frequency'=>'weekly','interest_rate'=>'0']);
        $r=app(RepaymentScheduleGenerator::class)->generate($p,'400.00',CarbonImmutable::parse('2026-08-01'));
        $this->assertCount(4,$r['installments']); $this->assertSame('2026-08-08',$r['installments'][0]['due_date']); $this->assertSame('2026-08-29',$r['installments'][3]['due_date']);
    }

    public function test_rounding_is_deterministic_and_schedule_reconciles_exactly(): void
    {
        $p=$this->product(['duration'=>3,'duration_unit'=>'weeks','repayment_frequency'=>'weekly','interest_rate'=>'10.000000']);
        $r=app(RepaymentScheduleGenerator::class)->generate($p,'100.00',CarbonImmutable::parse('2026-08-01'));
        $money=app(MoneyMath::class); $sum=0; foreach($r['installments'] as $i)$sum+=$money->toMinor($i['amount_due']);
        $this->assertSame($money->toMinor($r['summary']['total_repayment']),$sum); $this->assertSame('110.00',$r['summary']['total_repayment']);
    }

    public function test_custom_repayment_dates_are_supported(): void
    {
        $p=$this->product(['duration'=>2,'duration_unit'=>'months','repayment_frequency'=>'custom','interest_rate'=>'0']);
        $r=app(RepaymentScheduleGenerator::class)->generate($p,'300.00',CarbonImmutable::parse('2026-08-01'),['2026-08-20','2026-09-10','2026-10-01']);
        $this->assertCount(3,$r['installments']); $this->assertSame('2026-09-10',$r['installments'][1]['due_date']); $this->assertSame('300.00',$r['summary']['total_repayment']);
    }

    public function test_money_rounds_sub_cent_inputs_half_up_without_floating_point(): void
    {
        $money=app(MoneyMath::class); $this->assertSame(1,$money->toMinor('0.005000')); $this->assertSame(12346,$money->toMinor('123.456000')); $this->assertSame(1000,$money->percentageOf(10000,'10.000000'));
    }
}
