<?php

namespace App\Services\AgentSettlement;

use App\Models\Player;
use App\Models\AgentNode;
use App\Models\AgentProfile;
use Illuminate\Support\Facades\DB;

final class BetSettlementSnapshotBuilder
{
    /**
     * @return array{
     *   agent_node_id: int,
     *   agent_path: list<int>,
     *   chain_codes: list<string>,
     *   total_shares: array<string, float>,
     *   actual_shares: array<string, float>,
     *   rebate_rate: float,
     *   extra_rebate_rate: float,
     * }
     */
    public function buildForPlayer(Player $player, string $gameType = '*'): array
    {
        $agentNodeId = (int) $player->agent_node_id;
        if ($agentNodeId <= 0) {
            throw new \InvalidArgumentException('player_missing_agent');
        }

        $pathIds = [];
        $chainCodes = [];
        $totalShares = [];
        $nodeId = $agentNodeId;

        while ($nodeId > 0) {
            $node = AgentNode::query()->find($nodeId);
            if ($node === null) {
                break;
            }
            array_unshift($pathIds, (int) $node->id);
            $profile = AgentProfile::query()->where('agent_node_id', $node->id)->first();
            $code = (string) $node->code;
            $chainCodes[] = $code;
            $totalShares[$code] = (float) ($profile?->total_share_rate ?? 0);
            $nodeId = (int) ($node->parent_id ?? 0);
        }

        $orderedBottomUp = $chainCodes;
        $actual = $this->resolveActualShares($totalShares, $orderedBottomUp);

        $rebate = $this->resolvePlayerRebateRate((int) $player->id, $agentNodeId, $gameType);

        return [
            'agent_node_id' => $agentNodeId,
            'agent_path' => $pathIds,
            'chain_codes' => $orderedBottomUp,
            'total_shares' => $totalShares,
            'actual_shares' => $actual,
            'rebate_rate' => $rebate['rebate_rate'],
            'extra_rebate_rate' => $rebate['extra_rebate_rate'],
        ];
    }

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

        return $actual;
    }

    /**
     * @return array{rebate_rate: float, extra_rebate_rate: float}
     */
    private function resolvePlayerRebateRate(int $playerId, int $agentNodeId, string $gameType = '*'): array
    {
        $gameType = trim($gameType) !== '' ? trim($gameType) : '*';

        $row = DB::table('player_rebate_profiles')
            ->where('player_id', $playerId)
            ->where('game_type', $gameType)
            ->first();

        if ($row === null && $gameType !== '*') {
            $row = DB::table('player_rebate_profiles')
                ->where('player_id', $playerId)
                ->where('game_type', '*')
                ->first();
        }

        if ($row !== null && ! (bool) $row->inherit_from_agent) {
            return [
                'rebate_rate' => (float) $row->rebate_rate,
                'extra_rebate_rate' => (float) $row->extra_rebate_rate,
            ];
        }

        $profile = AgentProfile::query()->where('agent_node_id', $agentNodeId)->first();

        return [
            'rebate_rate' => (float) ($profile?->default_player_rebate ?? 0),
            'extra_rebate_rate' => 0.0,
        ];
    }
}
