<?php

namespace App\Services\CreditScore\Components;

use App\Services\CreditScore\ComponentResult;

class AffordabilityComponent
{
    public function __construct(private readonly array $configuration)
    {
    }

    public function calculate(array $data): ComponentResult
    {
        $income = (float) ($data['monthly_income'] ?? 0);
        $neutral = (float) ($this->configuration['thin_file']['neutral_component_score'] ?? 50);

        if ($income <= 0) {
            return new ComponentResult(
                'affordability',
                $neutral,
                false,
                ['missing_income' => true],
                [],
                ['Monthly income is unavailable, so affordability requires manual review.'],
            );
        }

        $monthlyDebtService = max(0, (float) ($data['monthly_debt_service'] ?? 0));
        $proposedMonthlyInstallment = max(0, (float) ($data['proposed_monthly_installment'] ?? 0));
        $activeOutstanding = max(0, (float) ($data['active_total_outstanding'] ?? 0));
        $activeLoanCount = max(0, (int) ($data['active_loan_count'] ?? 0));

        $debtServiceRatio = ($monthlyDebtService + $proposedMonthlyInstallment) / $income;
        $outstandingToIncomeRatio = $activeOutstanding / $income;

        $dsrScore = $this->scoreLowerIsBetter(
            $debtServiceRatio,
            $this->configuration['thresholds']['affordability']['debt_service_ratio'] ?? [],
            120,
        );
        $exposureScore = $this->scoreLowerIsBetter(
            $outstandingToIncomeRatio,
            $this->configuration['thresholds']['affordability']['outstanding_to_income_ratio'] ?? [],
            10,
        );

        $score = (0.65 * $dsrScore) + (0.35 * $exposureScore);
        $score -= min(10, max(0, $activeLoanCount - 1) * 3);
        $score = $this->clamp($score, 0, 100);

        $positive = [];
        $negative = [];
        $dsrThresholds = $this->configuration['thresholds']['affordability']['debt_service_ratio'] ?? [];

        if ($debtServiceRatio <= (float) ($dsrThresholds['excellent_max'] ?? 0.20)) {
            $positive[] = 'Current debt service is low relative to monthly income.';
        } elseif ($debtServiceRatio > (float) ($dsrThresholds['fair_max'] ?? 0.50)) {
            $negative[] = 'Debt service is high relative to monthly income.';
        }

        if ($activeLoanCount > 1) {
            $negative[] = "The customer currently has {$activeLoanCount} active loans.";
        }

        return new ComponentResult(
            'affordability',
            $score,
            true,
            [
                'missing_income' => false,
                'monthly_income' => round($income, 2),
                'monthly_debt_service' => round($monthlyDebtService, 2),
                'proposed_monthly_installment' => round($proposedMonthlyInstallment, 2),
                'active_total_outstanding' => round($activeOutstanding, 2),
                'active_loan_count' => $activeLoanCount,
                'debt_service_ratio' => round($debtServiceRatio, 4),
                'outstanding_to_income_ratio' => round($outstandingToIncomeRatio, 4),
            ],
            $positive,
            $negative,
        );
    }

    private function scoreLowerIsBetter(float $value, array $thresholds, float $declineFactor): float
    {
        $excellent = (float) ($thresholds['excellent_max'] ?? 0);
        $good = (float) ($thresholds['good_max'] ?? $excellent);
        $fair = (float) ($thresholds['fair_max'] ?? $good);

        if ($value <= $excellent) {
            return 100;
        }
        if ($value <= $good) {
            return 80;
        }
        if ($value <= $fair) {
            return 60;
        }

        return $this->clamp(60 - (($value - $fair) * $declineFactor), 0, 60);
    }

    private function clamp(float $value, float $minimum, float $maximum): float
    {
        return max($minimum, min($maximum, $value));
    }
}
