<?php

namespace App\Services\Draw;

use App\Models\Draw;
use App\Lottery\DrawStatus;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use Illuminate\Support\Collection;
use App\Lottery\DrawResultBatchStatus;
use App\Services\Jackpot\JackpotSummaryService;
use App\Services\LotterySettings;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

/**
 * 将已发布的 {@see DrawResultItem} 聚合成前端/文档约定结构。
 */
final class DrawResultViewService
{
    public function __construct(
        private readonly JackpotSummaryService $jackpotSummary,
    ) {}

    /**
     * 与 `docs/01-产品文档` GET /api/v1/results 示例键名对齐（1st/2nd/3rd/starter/consolation）。
     *
     * @return array{
     *   1st: string,
     *   2nd: string,
     *   3rd: string,
     *   starter: array<int, string>,
     *   consolation: array<int, string>
     * }
     */
    /** 已发布批次的头奖 4D 号码；未发布或缺失时返回空字符串。 */
    public function firstPrizeNumber4dForDraw(Draw $draw): string
    {
        $summary = $this->summarizeDraw($draw);
        if ($summary === null) {
            return '';
        }

        return (string) ($summary['results']['1st'] ?? '');
    }

    public function numbersFromItems(Collection $items): array
    {
        $byType = [
            'first' => [],
            'second' => [],
            'third' => [],
            'starter' => [],
            'consolation' => [],
        ];

        foreach ($items->sortBy(['prize_type', 'prize_index']) as $row) {
            /** @var DrawResultItem $row */
            $t = (string) $row->prize_type;
            if (! isset($byType[$t])) {
                continue;
            }
            $byType[$t][] = (string) $row->number_4d;
        }

        return [
            '1st' => $byType['first'][0] ?? '',
            '2nd' => $byType['second'][0] ?? '',
            '3rd' => $byType['third'][0] ?? '',
            'starter' => array_values($byType['starter']),
            'consolation' => array_values($byType['consolation']),
        ];
    }

    /**
     * 返回 null 若该期尚未有可展示的开奖采纳版本。
     *
     * @return array<string, mixed>|null
     */
    public function summarizeDraw(Draw $draw, ?string $currencyCode = null): ?array
    {
        $currencyCode = $this->normalizeCurrencyCode($currencyCode);
        $version = (int) $draw->current_result_version;
        if ($version < 1) {
            return null;
        }

        $batch = DrawResultBatch::query()
            ->where('draw_id', $draw->id)
            ->where('result_version', $version)
            ->where('status', DrawResultBatchStatus::Published->value)
            ->first();

        if ($batch === null) {
            return null;
        }

        $items = DrawResultItem::query()
            ->where('result_batch_id', $batch->id)
            ->orderBy('prize_type')
            ->orderBy('prize_index')
            ->get([
                'prize_type', 'prize_index', 'number_4d',
                'suffix_3d', 'suffix_2d', 'head_digit', 'tail_digit',
            ]);

        if ($items->isEmpty()) {
            return null;
        }

        $numbers = $this->numbersFromItems($items);

        return [
            'draw_id' => $draw->draw_no,
            'draw_no' => $draw->draw_no,
            'business_date' => $draw->business_date?->format('Y-m-d') ?? (string) $draw->business_date,
            'draw_time' => $draw->draw_time?->format('Y-m-d H:i:s'),
            'draw_time_iso' => $draw->draw_time?->toIso8601String(),
            'result_version' => $version,
            'result_source' => $draw->result_source,
            'jackpot_currency_code' => $currencyCode,
            'jackpot' => $this->jackpotSummary->summary($currencyCode),
            'results' => $numbers,
            'result_items' => $items->map(fn (DrawResultItem $r) => [
                'prize_type' => $r->prize_type,
                'prize_index' => (int) $r->prize_index,
                'number_4d' => $r->number_4d,
                'suffix_3d' => $r->suffix_3d,
                'suffix_2d' => $r->suffix_2d,
                'head_digit' => $r->head_digit !== null ? (int) $r->head_digit : null,
                'tail_digit' => $r->tail_digit !== null ? (int) $r->tail_digit : null,
            ])->values()->all(),
        ];
    }

    /**
     * @param  LengthAwarePaginator<int, Draw>  $paginator
     */
    public function decoratePaginator(LengthAwarePaginator $paginator, ?string $currencyCode = null): LengthAwarePaginator
    {
        $collection = $paginator->getCollection()->map(function (Draw $draw) use ($currencyCode): ?array {
            return $this->summarizeDraw($draw, $currencyCode);
        })->filter();

        $paginator->setCollection($collection->values());

        return $paginator;
    }

    /** 已发布开奖结果的可查询状态（对外展示往期）。 */
    public static function publishedDrawStatuses(): array
    {
        return [
            DrawStatus::Cooldown->value,
            DrawStatus::Settling->value,
            DrawStatus::Settled->value,
        ];
    }

    public function neighborsIsoTime(Draw $draw): array
    {
        $statuses = self::publishedDrawStatuses();
        $t = $draw->draw_time;
        $prevNo = null;
        $nextNo = null;

        if ($t !== null) {
            $prevNo = Draw::query()
                ->whereIn('status', $statuses)
                ->where('current_result_version', '>', 0)
                ->whereNotNull('draw_time')
                ->where('draw_time', '<', $t)
                ->orderByDesc('draw_time')
                ->value('draw_no');

            $nextNo = Draw::query()
                ->whereIn('status', $statuses)
                ->where('current_result_version', '>', 0)
                ->whereNotNull('draw_time')
                ->where('draw_time', '>', $t)
                ->orderBy('draw_time')
                ->value('draw_no');
        }

        return [
            'previous_draw_no' => $prevNo,
            'next_draw_no' => $nextNo,
        ];
    }

    private function normalizeCurrencyCode(?string $currencyCode): string
    {
        $code = strtoupper(substr(trim((string) ($currencyCode ?? '')), 0, 16));

        if ($code !== '') {
            return $code;
        }

        return LotterySettings::defaultCurrency();
    }
}
