<?php

namespace App\Services\AgentSettlement;

/**
 * 差额占成结算（设计文档 §11.4～§12.4）。
 */
final class ShareSettlementCalculator
{
    /**
     * @param  array<string, float|int>  $totalSharesByCode  自下而上，如 C,B,A（百分比 0-100）
     * @param  array<string, float|int>  $extraRebateByCode  谁设置谁承担
     * @param  list<string>  $chainFromPlayer  自下而上参与方 code，末位为平台侧用 platform
     */
    public function calculate(
        float $sharedNetWinLoss,
        array $totalSharesByCode,
        array $extraRebateByCode = [],
        float $gameWinLoss = 0,
        float $basicRebate = 0,
        array $chainFromPlayer = [],
    ): ShareSettlementResult {
        if ($gameWinLoss !== 0.0 || $basicRebate !== 0.0) {
            $extraTotal = array_sum(array_map(floatval(...), $extraRebateByCode));
            $playerNet = $gameWinLoss - $basicRebate - $extraTotal;
            $shared = $gameWinLoss - $basicRebate;
        } else {
            $playerNet = $sharedNetWinLoss - array_sum(array_map(floatval(...), $extraRebateByCode));
            $shared = $sharedNetWinLoss;
        }

        $ordered = $chainFromPlayer !== [] ? $chainFromPlayer : array_keys($totalSharesByCode);
        $actual = $this->resolveActualShares($totalSharesByCode, $ordered);

        $shareProfits = [];
        $finalProfits = [];
        foreach ($actual as $code => $rate) {
            $shareProfits[$code] = round($shared * ($rate / 100), 4);
            $extra = (float) ($extraRebateByCode[$code] ?? 0);
            $finalProfits[$code] = round($shareProfits[$code] - $extra, 4);
        }

        $tierSettlements = $this->buildTierSettlements($playerNet, $finalProfits, $ordered);

        return new ShareSettlementResult(
            playerNetSettlement: round($playerNet, 4),
            sharedNetWinLoss: round($shared, 4),
            shareProfits: $shareProfits,
            finalProfits: $finalProfits,
            tierSettlements: $tierSettlements,
        );
    }

    /**
     * @param  array<string, float|int>  $totalSharesByCode
     * @param  list<string>  $orderedBottomUp
     * @return array<string, float>
     */
    private function resolveActualShares(array $totalSharesByCode, array $orderedBottomUp): array
    {
        $actual = [];
        $prev = 0.0;
        foreach ($orderedBottomUp as $code) {
            $total = (float) ($totalSharesByCode[$code] ?? 0);
            $actual[$code] = max(0, $total - $prev);
            $prev = $total;
        }
        $topTotal = $prev;
        $actual['platform'] = max(0, 100 - $topTotal);

        return $actual;
    }

    /**
     * @param  array<string, float>  $finalProfits
     * @param  list<string>  $orderedBottomUp
     * @return array<string, float>
     */
    private function buildTierSettlements(float $playerNet, array $finalProfits, array $orderedBottomUp): array
    {
        $keys = ['P_to_'.($orderedBottomUp[0] ?? 'agent')];
        for ($i = 0; $i < count($orderedBottomUp) - 1; $i++) {
            $keys[] = $orderedBottomUp[$i].'_to_'.$orderedBottomUp[$i + 1];
        }
        if (count($orderedBottomUp) >= 1) {
            $last = $orderedBottomUp[count($orderedBottomUp) - 1];
            $keys[] = $last.'_to_platform';
        }

        $amount = $playerNet;
        $tier = [];
        if ($orderedBottomUp === []) {
            return $tier;
        }

        $tier['P_to_'.$orderedBottomUp[0]] = round($amount, 4);
        for ($i = 0; $i < count($orderedBottomUp); $i++) {
            $code = $orderedBottomUp[$i];
            $keep = (float) ($finalProfits[$code] ?? 0);
            $amount = round($amount - $keep, 4);
            if ($i < count($orderedBottomUp) - 1) {
                $next = $orderedBottomUp[$i + 1];
                $tier[$code.'_to_'.$next] = $amount;
            } else {
                $tier[$code.'_to_platform'] = $amount;
            }
        }

        return $tier;
    }
}
