<?php

namespace App\Services\Draw;

use App\Models\Draw;
use App\Models\AdminUser;
use App\Lottery\DrawStatus;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use Illuminate\Support\Facades\DB;
use App\Lottery\DrawResultSourceType;
use App\Lottery\DrawResultBatchStatus;

final class DrawManualResultService
{
    /**
     * @param  list<array{prize_type: string, prize_index: int, number_4d: string}>  $items
     */
    public function createPendingBatch(Draw $draw, AdminUser $admin, array $items): DrawResultBatch
    {
        return DB::transaction(function () use ($draw, $admin, $items): DrawResultBatch {
            /** @var Draw $locked */
            $locked = Draw::query()->whereKey($draw->id)->lockForUpdate()->firstOrFail();
            if (! in_array($locked->status, [DrawStatus::Closed->value, DrawStatus::Review->value], true)) {
                throw new \RuntimeException('draw_not_editable');
            }
            if ($locked->settle_version > 0 || $locked->status === DrawStatus::Settled->value) {
                throw new \RuntimeException('draw_already_settled');
            }

            if (DrawResultBatch::query()
                ->where('draw_id', $locked->id)
                ->where('status', DrawResultBatchStatus::PendingReview->value)
                ->exists()) {
                throw new \RuntimeException('draw_pending_result_batch_exists');
            }

            $nextVersion = max(1, (int) $locked->current_result_version + 1);
            $batch = DrawResultBatch::query()->create([
                'draw_id' => $locked->id,
                'result_version' => $nextVersion,
                'source_type' => DrawResultSourceType::Manual->value,
                'rng_seed_hash' => null,
                'raw_seed_encrypted' => null,
                'status' => DrawResultBatchStatus::PendingReview->value,
                'created_by' => $admin->id,
                'confirmed_by' => null,
                'confirmed_at' => null,
            ]);

            foreach ($this->sortByLayout($items) as $item) {
                $number = (string) $item['number_4d'];
                DrawResultItem::query()->create([
                    'draw_id' => $locked->id,
                    'result_batch_id' => $batch->id,
                    'prize_type' => $item['prize_type'],
                    'prize_index' => (int) $item['prize_index'],
                    'number_4d' => $number,
                    'suffix_3d' => substr($number, -3),
                    'suffix_2d' => substr($number, -2),
                    'head_digit' => (int) substr($number, 0, 1),
                    'tail_digit' => (int) substr($number, 3, 1),
                ]);
            }

            $locked->forceFill([
                'status' => DrawStatus::Review->value,
                'result_source' => DrawResultSourceType::Manual->value,
            ])->save();

            return $batch->fresh(['items']);
        });
    }

    /**
     * @param  list<array{prize_type: string, prize_index: int, number_4d: string}>  $items
     * @return list<array{prize_type: string, prize_index: int, number_4d: string}>
     */
    private function sortByLayout(array $items): array
    {
        $order = [];
        foreach (DrawPrizeLayout::slots() as $i => $slot) {
            $order[$slot['prize_type'].':'.$slot['prize_index']] = $i;
        }

        usort($items, fn (array $a, array $b): int => ($order[$a['prize_type'].':'.$a['prize_index']] ?? 99)
            <=> ($order[$b['prize_type'].':'.$b['prize_index']] ?? 99));

        return $items;
    }
}
