<?php

declare(strict_types=1);

namespace Kylin\RiskControl\Services;

/**
 * 评分卡模型
 *
 * 依据 GB/T 32319-2015 银行业产品描述指南，实现 A/B/C 三类评分卡。
 *
 * 评分范围：300-850 分（行业标准）
 * 权重分配：A 卡 50%，B 卡 30%，C 卡 20%
 *
 * 使用方式：
 *   $score = ScorecardModel::calculate(['a_score' => 600, 'b_score' => 500, 'c_score' => 400]);
 */
final class ScorecardModel
{
    /** 评分范围 */
    public const SCORE_MIN = 300;
    public const SCORE_MAX = 850;

    /** 权重分配 */
    public const WEIGHT_A = 0.50; // 申请评分卡
    public const WEIGHT_B = 0.30; // 行为评分卡
    public const WEIGHT_C = 0.20; // 催收评分卡

    /** 风险等级 */
    public const LEVEL_LOW = 'low';
    public const LEVEL_MODERATE = 'moderate';
    public const LEVEL_HIGH = 'high';
    public const LEVEL_CRITICAL = 'critical';

    /**
     * 计算综合评分
     *
     * @param array{ a_score?: float, b_score?: float, c_score?: float } $scores
     * @return array{ score: float, level: string, label: string, details: array }
     */
    public static function calculate(array $scores): array
    {
        $aScore = $scores['a_score'] ?? 0;
        $bScore = $scores['b_score'] ?? 0;
        $cScore = $scores['c_score'] ?? 0;

        $total = ($aScore * self::WEIGHT_A)
            + ($bScore * self::WEIGHT_B)
            + ($cScore * self::WEIGHT_C);

        $total = max(self::SCORE_MIN, min(self::SCORE_MAX, $total));

        $level = self::classify($total);

        return [
            'score' => round($total, 2),
            'level' => $level,
            'label' => self::label($level),
            'details' => [
                'a_card' => $aScore,
                'b_card' => $bScore,
                'c_card' => $cScore,
            ],
        ];
    }

    /**
     * 根据评分划分风险等级
     *
     * 依据行业标准：
     *  - 650-850：低风险
     *  - 500-649：中等风险
     *  - 350-499：高风险
     *  - 300-349：极高风险
     */
    public static function classify(float $score): string
    {
        return match (true) {
            $score >= 650 => self::LEVEL_LOW,
            $score >= 500 => self::LEVEL_MODERATE,
            $score >= 350 => self::LEVEL_HIGH,
            default => self::LEVEL_CRITICAL,
        };
    }

    /**
     * A 卡（申请评分卡）计算
     *
     * 评估维度：身份核实、征信查询、多头借贷、收入水平
     */
    public static function calculateACard(array $dimensions): float
    {
        $weights = [
            'identity_verified' => 0.25,  // 身份核实
            'credit_score' => 0.35,       // 征信评分
            'multi_loan_count' => 0.20,   // 多头借贷
            'income_level' => 0.20,       // 收入水平
        ];

        return self::weightedScore($dimensions, $weights);
    }

    /**
     * B 卡（行为评分卡）计算
     *
     * 评估维度：还款行为、消费习惯、账户活跃度、额度使用率
     */
    public static function calculateBCard(array $dimensions): float
    {
        $weights = [
            'repayment_behavior' => 0.40,  // 还款行为
            'consumption_habit' => 0.25,   // 消费习惯
            'account_activity' => 0.15,    // 账户活跃度
            'credit_utilization' => 0.20,  // 额度使用率
        ];

        return self::weightedScore($dimensions, $weights);
    }

    /**
     * C 卡（催收评分卡）计算
     *
     * 评估维度：逾期天数、还款意愿、联系方式有效性、历史催收效果
     */
    public static function calculateCCard(array $dimensions): float
    {
        $weights = [
            'overdue_days' => 0.35,        // 逾期天数
            'repayment_willingness' => 0.30, // 还款意愿
            'contact_validity' => 0.15,    // 联系方式有效性
            'collection_history' => 0.20,  // 历史催收效果
        ];

        return self::weightedScore($dimensions, $weights);
    }

    /**
     * 加权计算
     */
    private static function weightedScore(array $dimensions, array $weights): float
    {
        $score = 0;
        $totalWeight = 0;

        foreach ($weights as $key => $weight) {
            if (isset($dimensions[$key])) {
                $score += $dimensions[$key] * $weight;
                $totalWeight += $weight;
            }
        }

        if ($totalWeight > 0) {
            $score /= $totalWeight;
        }

        return round(max(self::SCORE_MIN, min(self::SCORE_MAX, $score)), 2);
    }

    /**
     * 风险等级标签
     */
    public static function label(string $level): string
    {
        return match ($level) {
            self::LEVEL_LOW => '低风险',
            self::LEVEL_MODERATE => '中等风险',
            self::LEVEL_HIGH => '高风险',
            self::LEVEL_CRITICAL => '极高风险',
            default => '未知',
        };
    }
}