<?php

namespace App\Services\Ticket;

use App\Models\Draw;
use App\Models\Player;
use App\Lottery\ErrorCode;
use App\Exceptions\TicketOperationException;
use App\Services\Draw\DrawHallSnapshotBuilder;
use App\Support\PlayerFundingMode;

final class TicketPreviewService
{
    public function __construct(
        private readonly PlayCatalogResolver $catalogResolver,
        private readonly PlayRuleEngine $ruleEngine,
        private readonly TicketLineInstantRebateApplicator $rebateApplicator,
        private readonly RiskPoolService $riskPoolService,
        private readonly DrawHallSnapshotBuilder $drawHallSnapshot,
    ) {}

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>
     */
    public function preview(Player $player, array $payload): array
    {
        $drawNo = trim((string) ($payload['draw_id'] ?? ''));
        $draw = $drawNo === ''
            ? null
            : Draw::query()->where('draw_no', $drawNo)->first();
        if ($draw === null) {
            throw new TicketOperationException('draw_not_found', ErrorCode::BetInvalidDraw->value);
        }
        if (! $this->drawHallSnapshot->isBettingOpen($draw)) {
            throw new TicketOperationException('draw_closed', ErrorCode::DrawClosed->value);
        }

        $currencyCode = strtoupper((string) $payload['currency_code']);
        $lines = [];
        $totalBet = 0;
        $totalRebate = 0;
        $totalActualDeduct = 0;
        $totalEstimatedPayout = 0;
        $warningRows = [];
        $closedPlayCleanupRows = [];

        foreach ((array) $payload['lines'] as $index => $line) {
            try {
                $resolved = $this->catalogResolver->resolve((string) $line['play_code'], $currencyCode);
            } catch (TicketOperationException $e) {
                if ($e->lotteryCode === ErrorCode::PlayModeClosed->value) {
                    $closedPlayCleanupRows[] = [
                        'client_line_no' => $index + 1,
                        'play_code' => (string) ($line['play_code'] ?? ''),
                    ];

                    continue;
                }

                throw $e;
            }
            $evaluated = $this->ruleEngine->evaluateLine(
                (array) $line,
                $resolved['play_config'],
                $resolved['odds_items'],
            );
            $evaluated = $this->rebateApplicator->apply($player, $evaluated);

            $locks = array_map(fn (array $combo): array => [
                'number_4d' => $combo['number_4d'],
                'amount' => $combo['estimated_payout'],
            ], $evaluated['combinations']);
            $riskPreview = $this->riskPoolService->preview((int) $draw->id, $locks);
            foreach ($riskPreview as $riskRow) {
                if ($riskRow['warning']) {
                    $warningRows[] = [
                        'number_4d' => $riskRow['number_4d'],
                        'message' => '该号码赔付池已使用 80% 以上，可能即将售罄',
                    ];
                }
            }

            $rebateAmount = $this->rebateApplicator->displayRebateAmount($player, $evaluated);
            $totalBet += (int) $evaluated['total_bet_amount'];
            $totalRebate += $rebateAmount;
            $totalActualDeduct += (int) $evaluated['actual_deduct_amount'];
            $totalEstimatedPayout += (int) $evaluated['estimated_max_payout'];

            $lines[] = [
                'client_line_no' => $index + 1,
                'number' => $evaluated['original_number'],
                'play_code' => $evaluated['play_code'],
                'normalized_number' => $evaluated['normalized_number'],
                'combination_count' => $evaluated['combination_count'],
                'total_bet_amount' => $evaluated['total_bet_amount'],
                'rebate_rate' => $evaluated['rebate_rate_snapshot'],
                'rebate_amount' => $rebateAmount,
                'actual_deduct_amount' => $evaluated['actual_deduct_amount'],
                'estimated_max_payout' => $evaluated['estimated_max_payout'],
                'risk_status' => 'ok',
                'warnings' => [],
                'rule_snapshot_json' => $evaluated['rule_snapshot_json'],
            ];
        }

        if ($closedPlayCleanupRows !== []) {
            throw new TicketOperationException(
                'play_closed_need_cleanup',
                ErrorCode::PlayModeClosed->value,
                400,
                [
                    'cleanup_hint' => '玩法已关闭，相关注项已清理',
                    'cleanup_lines' => $closedPlayCleanupRows,
                ],
            );
        }

        $nowUtc = now()->utc();

        return [
            'draw' => [
                'draw_id' => $draw->draw_no,
                'status' => $this->drawHallSnapshot->effectiveHallDisplayStatus($draw, $nowUtc),
                'db_status' => $draw->status,
            ],
            'config_versions' => $this->catalogResolver->currentActiveVersionStamp(),
            'summary' => [
                'total_bet_amount' => $totalBet,
                'total_rebate_amount' => $totalRebate,
                'total_actual_deduct' => $totalActualDeduct,
                'total_estimated_payout' => $totalEstimatedPayout,
                'instant_rebate_applied' => ! PlayerFundingMode::usesCredit($player),
            ],
            'lines' => $lines,
            'warnings' => $warningRows,
        ];
    }
}
