<?php

namespace App\Services\AgentSettlement;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/** 结算列表：玩家 / 代理 / 注单关联字段 enrichment。 */
final class SettlementPartyEnrichment
{
    /**
     * @param  Collection<int, object>  $agents  keyed by id
     * @return array{
     *   direct_agent_id: int|null,
     *   direct_agent_label: string|null,
     *   parent_agent_id: int|null,
     *   parent_agent_label: string|null,
     * }
     */
    public function agentLineLabels(?int $directAgentId, Collection $agents): array
    {
        if ($directAgentId === null || $directAgentId <= 0) {
            return [
                'direct_agent_id' => null,
                'direct_agent_label' => null,
                'parent_agent_id' => null,
                'parent_agent_label' => null,
            ];
        }

        $direct = $agents->get($directAgentId);
        $directLabel = $this->formatAgent($direct, $directAgentId);
        $parentId = $direct !== null ? (int) ($direct->parent_id ?? 0) : 0;
        $parent = $parentId > 0 ? $agents->get($parentId) : null;

        return [
            'direct_agent_id' => $directAgentId,
            'direct_agent_label' => $directLabel,
            'parent_agent_id' => $parentId > 0 ? $parentId : null,
            'parent_agent_label' => $parentId > 0 ? $this->formatAgent($parent, $parentId) : null,
        ];
    }

    /**
     * @param  list<int>  $agentIds
     * @return Collection<int, object>
     */
    public function loadAgents(array $agentIds): Collection
    {
        $ids = array_values(array_unique(array_filter($agentIds, static fn (int $id): bool => $id > 0)));
        if ($ids === []) {
            return collect();
        }

        $rows = DB::table('agent_nodes')->whereIn('id', $ids)->get()->keyBy('id');
        $parentIds = $rows
            ->pluck('parent_id')
            ->map(static fn ($id): int => (int) $id)
            ->filter(static fn (int $id): bool => $id > 0)
            ->unique()
            ->values()
            ->all();

        $missingParents = array_diff($parentIds, $ids);
        if ($missingParents !== []) {
            $parents = DB::table('agent_nodes')->whereIn('id', $missingParents)->get()->keyBy('id');
            foreach ($parents as $id => $row) {
                $rows->put((int) $id, $row);
            }
        }

        return $rows;
    }

    /**
     * @param  list<int>  $ticketItemIds
     * @return array<int, array{play_code: string|null, draw_no: string|null, ticket_item_id: int, actual_deduct_amount: int}>
     */
    public function loadTicketRefs(array $ticketItemIds): array
    {
        $ids = array_values(array_unique(array_filter($ticketItemIds, static fn (int $id): bool => $id > 0)));
        if ($ids === []) {
            return [];
        }

        $map = [];
        foreach (DB::table('ticket_items as ti')
            ->leftJoin('draws as d', 'd.id', '=', 'ti.draw_id')
            ->whereIn('ti.id', $ids)
            ->select(['ti.id', 'ti.play_code', 'ti.actual_deduct_amount', 'd.draw_no'])
            ->get() as $row) {
            $map[(int) $row->id] = [
                'ticket_item_id' => (int) $row->id,
                'play_code' => $row->play_code !== null ? (string) $row->play_code : null,
                'draw_no' => $row->draw_no !== null ? (string) $row->draw_no : null,
                'actual_deduct_amount' => (int) ($row->actual_deduct_amount ?? 0),
            ];
        }

        return $map;
    }

    /** 结算展示：仅代理名称；编号为内部标识，无名称时才回退 code。 */
    public function formatAgent(?object $agent, int $fallbackId): string
    {
        if ($agent === null) {
            return "agent#{$fallbackId}";
        }

        $name = trim((string) ($agent->name ?? ''));
        if ($name !== '') {
            return $name;
        }

        $code = trim((string) ($agent->code ?? ''));

        return $code !== '' ? $code : "agent#{$fallbackId}";
    }

    public function formatPlayerUsername(?object $player): ?string
    {
        if ($player === null) {
            return null;
        }

        $username = trim((string) ($player->username ?? ''));

        return $username !== '' ? $username : null;
    }

    public function formatPlayerSiteId(?object $player): ?string
    {
        if ($player === null) {
            return null;
        }

        $sitePlayerId = trim((string) ($player->site_player_id ?? ''));

        return $sitePlayerId !== '' ? $sitePlayerId : null;
    }

    public function formatCounterpartyLabel(string $type, int $id, Collection $agents): string
    {
        if ($type === 'platform' || $id <= 0) {
            return 'platform';
        }

        if ($type === 'agent') {
            return $this->formatAgent($agents->get($id), $id);
        }

        return "{$type}#{$id}";
    }

    /**
     * @param  iterable<int, object>  $rows
     * @return list<array<string, mixed>>
     */
    public function enrichBillRows(iterable $rows): array
    {
        $items = collect($rows);
        if ($items->isEmpty()) {
            return [];
        }

        $playerIds = [];
        $agentIds = [];
        foreach ($items as $row) {
            if ((string) $row->owner_type === 'player') {
                $playerIds[] = (int) $row->owner_id;
            } elseif ((string) $row->owner_type === 'agent') {
                $agentIds[] = (int) $row->owner_id;
            }
            if ((string) $row->counterparty_type === 'agent' && (int) $row->counterparty_id > 0) {
                $agentIds[] = (int) $row->counterparty_id;
            }
        }

        $players = $playerIds !== []
            ? DB::table('players')
                ->whereIn('id', array_unique($playerIds))
                ->select(['id', 'username', 'site_player_id', 'agent_node_id', 'funding_mode', 'auth_source'])
                ->get()
                ->keyBy('id')
            : collect();

        foreach ($players as $player) {
            $aid = (int) ($player->agent_node_id ?? 0);
            if ($aid > 0) {
                $agentIds[] = $aid;
            }
        }

        $agents = $this->loadAgents($agentIds);

        $out = [];
        foreach ($items as $row) {
            $out[] = $this->enrichBillRowWithLookups($row, $players, $agents);
        }

        return $out;
    }

    /** @return array<string, mixed> */
    public function enrichBillRow(object $row): array
    {
        return $this->enrichBillRows([$row])[0];
    }

    /**
     * @param  Collection<int, object>  $players
     * @param  Collection<int, object>  $agents
     * @return array<string, mixed>
     */
    private function enrichBillRowWithLookups(object $row, Collection $players, Collection $agents): array
    {
        $item = (array) $row;
        $ownerType = (string) $row->owner_type;
        $counterType = (string) $row->counterparty_type;
        $counterId = (int) $row->counterparty_id;

        $item['owner_label'] = $this->legacyOwnerLabel($ownerType, (int) $row->owner_id, $players, $agents);
        $item['counterparty_label'] = $this->formatCounterpartyLabel($counterType, $counterId, $agents);

        $item['player_username'] = null;
        $item['player_site_player_id'] = null;
        $item['player_id_display'] = null;
        $item['direct_agent_label'] = null;
        $item['superior_agent_label'] = null;
        $item['owner_party_label'] = null;

        if ($ownerType === 'player') {
            $player = $players->get((int) $row->owner_id);
            $item['player_username'] = $this->formatPlayerUsername($player);
            $item['player_site_player_id'] = $this->formatPlayerSiteId($player);
            $item['player_id_display'] = (int) $row->owner_id;
            $item['owner_funding_mode'] = $player !== null ? (string) ($player->funding_mode ?? '') : null;
            $item['owner_auth_source'] = $player !== null ? $player->auth_source : null;

            $directId = $counterType === 'agent' ? $counterId : (int) ($player->agent_node_id ?? 0);
            $line = $this->agentLineLabels($directId > 0 ? $directId : null, $agents);
            $item['direct_agent_label'] = $line['direct_agent_label'];
            $item['superior_agent_label'] = $line['parent_agent_label'];
        } elseif ($ownerType === 'agent') {
            $ownerAgentId = (int) $row->owner_id;
            $item['owner_party_label'] = $this->formatAgent($agents->get($ownerAgentId), $ownerAgentId);
            $item['superior_agent_label'] = $counterType === 'platform'
                ? 'platform'
                : $this->formatCounterpartyLabel($counterType, $counterId, $agents);
        }

        return $item;
    }

    /**
     * @param  Collection<int, object>  $players
     * @param  Collection<int, object>  $agents
     */
    private function legacyOwnerLabel(
        string $type,
        int $id,
        Collection $players,
        Collection $agents,
    ): string {
        if ($type === 'player') {
            $player = $players->get($id);

            return $player !== null
                ? (string) ($player->username ?: $player->site_player_id ?: "player#{$id}")
                : "player#{$id}";
        }

        if ($type === 'agent') {
            return $this->formatAgent($agents->get($id), $id);
        }

        return "{$type}#{$id}";
    }
}
