<?php

namespace App\Services\Draw;

use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Models\DrawResultItem;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;

/**
 * RNG 种子：CSPRNG 采集、SHA-256 摘要、Laravel 加密落库、确定性派生 4 位号码（算法 v1）。
 *
 * 验收：解密种子 → sha256(seed_hex) === rng_seed_hash → 复算 23 组号码与 draw_result_items 一致。
 */
final class DrawRngSeedDerivation
{
    public const ALGORITHM_VERSION = 'v1';

    /** 生成 32 字节随机种子（十六进制，64 字符） */
    public static function generateSeedHex(): string
    {
        return bin2hex(random_bytes(32));
    }

    public static function hashSeedHex(string $seedHex): string
    {
        return hash('sha256', $seedHex);
    }

    public static function encryptSeedHex(string $seedHex): string
    {
        return Crypt::encryptString($seedHex);
    }

    public static function decryptSeedHex(string $encrypted): string
    {
        try {
            return Crypt::decryptString($encrypted);
        } catch (DecryptException $e) {
            throw new \InvalidArgumentException('RNG seed decrypt failed', 0, $e);
        }
    }

    /**
     * 由种子确定性派生第 $slotIndex 槽位的 4 位号码（0000–9999）。
     */
    public static function deriveNumber4d(string $seedHex, int $drawId, int $slotIndex): string
    {
        $seedBinary = hex2bin($seedHex);
        if ($seedBinary === false || strlen($seedBinary) !== 32) {
            throw new \InvalidArgumentException('RNG seed must be 64 hex chars (32 bytes).');
        }

        $message = self::ALGORITHM_VERSION.'|draw:'.$drawId.'|slot:'.$slotIndex;
        $digest = hash_hmac('sha256', $message, $seedBinary, true);
        $chunk = substr($digest, 0, 4);
        $unpacked = unpack('V', $chunk);
        $value = ((int) ($unpacked[1] ?? 0)) % 10_000;

        return str_pad((string) $value, 4, '0', STR_PAD_LEFT);
    }

    /**
     * @return list<array{prize_type: string, prize_index: int, number_4d: string, suffix_3d: string, suffix_2d: string, head_digit: int|null, tail_digit: int|null}>
     */
    public static function deriveAllSlotRows(string $seedHex, int $drawId): array
    {
        $rows = [];
        foreach (DrawPrizeLayout::slots() as $slotIndex => $slot) {
            $num = self::deriveNumber4d($seedHex, $drawId, $slotIndex);
            $rows[] = [
                'prize_type' => $slot['prize_type'],
                'prize_index' => $slot['prize_index'],
                'number_4d' => $num,
                'suffix_3d' => substr($num, -3),
                'suffix_2d' => substr($num, -2),
                'head_digit' => $num !== '' ? (int) substr($num, 0, 1) : null,
                'tail_digit' => $num !== '' ? (int) substr($num, 3, 1) : null,
            ];
        }

        return $rows;
    }

    /** 审计：校验批次种子摘要、密文可解密且号码可由种子复算。 */
    public static function verifyBatchAudit(DrawResultBatch $batch, Draw $draw): bool
    {
        if ($batch->source_type !== 'rng') {
            return false;
        }

        $encrypted = $batch->raw_seed_encrypted;
        $hash = $batch->rng_seed_hash;
        if (! is_string($encrypted) || $encrypted === '' || ! is_string($hash) || $hash === '') {
            return false;
        }

        try {
            $seedHex = self::decryptSeedHex($encrypted);
        } catch (\InvalidArgumentException) {
            return false;
        }

        if (self::hashSeedHex($seedHex) !== $hash) {
            return false;
        }

        $expected = self::deriveAllSlotRows($seedHex, (int) $draw->id);
        $items = $batch->items()->get();

        if ($items->count() !== count($expected)) {
            return false;
        }

        foreach ($expected as $row) {
            $item = $items->first(fn (DrawResultItem $i) => $i->prize_type === $row['prize_type']
                && (int) $i->prize_index === $row['prize_index']);
            if ($item === null || $item->number_4d !== $row['number_4d']) {
                return false;
            }
        }

        return true;
    }
}
