<?php

namespace App\Services\Draw;

use Carbon\Carbon;
use App\Models\Draw;
use App\Services\LotterySettings;
use Illuminate\Support\Facades\DB;

/**
 * 管理员手动创建期号（可指定开奖/封盘/开始时间）。
 */
final class DrawManualCreateService
{
    public function __construct(
        private readonly DrawTimelineBuilder $timeline,
    ) {}

    /**
     * @param  array{
     *     draw_time: string,
     *     start_time?: string|null,
     *     close_time?: string|null,
     *     draw_no?: string|null,
     *     business_date?: string|null,
     *     sequence_no?: int|null,
     * }  $input
     */
    public function create(array $input, ?Carbon $now = null): Draw
    {
        $tz = LotterySettings::drawTimezone();
        $nowUtc = ($now ?? Carbon::now())->utc();

        $drawLocal = $this->parseInTimezone((string) $input['draw_time'], $tz);
        $startLocal = isset($input['start_time']) && $input['start_time'] !== null && $input['start_time'] !== ''
            ? $this->parseInTimezone((string) $input['start_time'], $tz)
            : null;
        $closeLocal = isset($input['close_time']) && $input['close_time'] !== null && $input['close_time'] !== ''
            ? $this->parseInTimezone((string) $input['close_time'], $tz)
            : null;

        if ($startLocal === null || $closeLocal === null) {
            $defaults = $this->timeline->windowsFromDrawLocal($drawLocal);
            $startLocal ??= $defaults['start_local'];
            $closeLocal ??= $defaults['close_local'];
        }

        if (! $startLocal->lt($closeLocal) || ! $closeLocal->lt($drawLocal)) {
            throw new \RuntimeException('draw_timeline_invalid');
        }

        $businessDate = isset($input['business_date']) && $input['business_date'] !== ''
            ? (string) $input['business_date']
            : $drawLocal->format('Y-m-d');

        $sequenceNo = isset($input['sequence_no']) && $input['sequence_no'] !== null
            ? max(1, (int) $input['sequence_no'])
            : $this->nextSequenceForDate($businessDate);

        $drawNo = isset($input['draw_no']) && trim((string) $input['draw_no']) !== ''
            ? trim((string) $input['draw_no'])
            : $this->timeline->drawNo($businessDate, $sequenceNo);

        if (Draw::query()->where('draw_no', $drawNo)->exists()) {
            throw new \RuntimeException('draw_no_exists');
        }

        $built = $this->timeline->buildFromLocals($startLocal, $closeLocal, $drawLocal, $nowUtc);

        return DB::transaction(function () use (
            $drawNo,
            $businessDate,
            $sequenceNo,
            $built,
        ): Draw {
            return Draw::query()->create([
                'draw_no' => $drawNo,
                'business_date' => $businessDate,
                'sequence_no' => $sequenceNo,
                'status' => $built['status'],
                'start_time' => $built['start_utc'],
                'close_time' => $built['close_utc'],
                'draw_time' => $built['draw_utc'],
                'cooling_end_time' => null,
                'result_source' => null,
                'current_result_version' => 0,
                'settle_version' => 0,
                'is_reopened' => false,
            ]);
        });
    }

    private function parseInTimezone(string $value, string $tz): Carbon
    {
        return Carbon::parse($value, $tz);
    }

    private function nextSequenceForDate(string $businessDate): int
    {
        $max = (int) Draw::query()
            ->where('business_date', $businessDate)
            ->max('sequence_no');

        return $max + 1;
    }
}
