<?php

declare(strict_types=1);

namespace app\common\library\game;

use app\common\service\GameHotDataRedis;

/**
 * 连胜奖励（game_config.streak_win_reward）：按「连胜档位」1～10 配置赔率系数与是否大奖。
 *
 * 派彩：total_amount × odds_factor（与后台「连胜奖励」表一致，不再额外 ×33）。
 */
final class StreakWinReward
{
    public const CONFIG_KEY = 'streak_win_reward';

    /** @var list<array{streak: int, odds_factor: int, is_jackpot: bool}>|null */
    private static ?array $cache = null;

    public static function clearCache(): void
    {
        self::$cache = null;
    }

    /**
     * @return list<array{streak: int, odds_factor: int, is_jackpot: bool}>
     */
    public static function defaultRows(): array
    {
        $out = [];
        for ($s = 1; $s <= 10; $s++) {
            $out[] = [
                'streak'       => $s,
                'odds_factor'  => $s,
                'is_jackpot'   => $s === 10,
            ];
        }

        return $out;
    }

    /**
     * @param mixed $raw
     *
     * @return list<array{streak: int, odds_factor: int, is_jackpot: bool}>
     */
    public static function parseFromConfigValue($raw): array
    {
        if (!is_string($raw) || trim($raw) === '') {
            return self::defaultRows();
        }
        $decoded = json_decode($raw, true);
        if (!is_array($decoded)) {
            return self::defaultRows();
        }
        $list = $decoded['rows'] ?? $decoded;
        if (!is_array($list)) {
            return self::defaultRows();
        }
        $byStreak = [];
        foreach ($list as $row) {
            if (!is_array($row)) {
                continue;
            }
            $streak = isset($row['streak']) && is_numeric($row['streak']) ? (int) $row['streak'] : 0;
            if ($streak < 1 || $streak > 10) {
                continue;
            }
            $factor = isset($row['odds_factor']) && is_numeric($row['odds_factor']) ? (int) $row['odds_factor'] : $streak;
            if ($factor < 1) {
                $factor = 1;
            }
            $jack = !empty($row['is_jackpot']);
            $byStreak[$streak] = [
                'streak'      => $streak,
                'odds_factor' => $factor,
                'is_jackpot'  => $jack,
            ];
        }
        $out = [];
        for ($s = 1; $s <= 10; $s++) {
            $out[] = $byStreak[$s] ?? [
                'streak'      => $s,
                'odds_factor' => $s,
                'is_jackpot'  => $s === 10,
            ];
        }

        return $out;
    }

    /**
     * 从库加载并缓存
     *
     * @return list<array{streak: int, odds_factor: int, is_jackpot: bool}>
     */
    public static function loadRows(): array
    {
        if (self::$cache !== null) {
            return self::$cache;
        }
        $row = GameHotDataRedis::gameConfigRow(self::CONFIG_KEY);
        self::$cache = self::parseFromConfigValue($row['config_value'] ?? null);

        return self::$cache;
    }

    /**
     * 当前玩家本局适用赔率（非全表）：按 current_streak 解析下一注中奖将使用的档位。
     *
     * @return array{current_streak: int, streak_level: int, odds_factor: int, is_jackpot: bool}
     */
    public static function playerBetOddsForCurrentStreak(int $currentStreak): array
    {
        if ($currentStreak < 0) {
            $currentStreak = 0;
        }
        $row = self::rowForStreakAtBet($currentStreak);

        return [
            'current_streak' => $currentStreak,
            'streak_level' => self::levelFromStreakAtBet($currentStreak),
            'odds_factor' => (int) ($row['odds_factor'] ?? 1),
            'is_jackpot' => ($row['is_jackpot'] ?? false) === true,
        ];
    }

    /**
     * streak_at_bet 为下注时快照（0 表示尚未连胜）；档位取 min(streak_at_bet+1, 10)。
     */
    public static function levelFromStreakAtBet(int $streakAtBet): int
    {
        $level = $streakAtBet + 1;
        if ($level < 1) {
            $level = 1;
        }
        if ($level > 10) {
            $level = 10;
        }

        return $level;
    }

    /**
     * @return array{streak: int, odds_factor: int, is_jackpot: bool}
     */
    public static function rowForStreakAtBet(int $streakAtBet): array
    {
        $level = self::levelFromStreakAtBet($streakAtBet);
        foreach (self::loadRows() as $row) {
            if ((int) ($row['streak'] ?? 0) === $level) {
                return $row;
            }
        }

        return [
            'streak'      => $level,
            'odds_factor' => $level,
            'is_jackpot'  => $level === 10,
        ];
    }

    public static function isJackpotForStreakAtBet(int $streakAtBet): bool
    {
        return self::rowForStreakAtBet($streakAtBet)['is_jackpot'] === true;
    }

    /**
     * 返回该注单适用的「赔率乘数」字符串（= 配置档位的 odds_factor），供 bcmul(total_amount, ..., 2)。
     */
    public static function totalOddsMultiplierForStreakAtBet(int $streakAtBet): string
    {
        $factor = (int) self::rowForStreakAtBet($streakAtBet)['odds_factor'];
        if ($factor < 1) {
            $factor = 1;
        }

        return bcadd((string) $factor, '0', 2);
    }

    /**
     * @param list<array{streak: int, odds_factor: int, is_jackpot: bool}> $rows
     */
    public static function encodeForDb(array $rows): string
    {
        $normalized = [];
        for ($s = 1; $s <= 10; $s++) {
            $found = null;
            foreach ($rows as $r) {
                if (!is_array($r)) {
                    continue;
                }
                $st = isset($r['streak']) && is_numeric($r['streak']) ? (int) $r['streak'] : 0;
                if ($st === $s) {
                    $found = $r;
                    break;
                }
            }
            if ($found === null) {
                $normalized[] = [
                    'streak'      => $s,
                    'odds_factor' => $s,
                    'is_jackpot'  => $s === 10,
                ];
            } else {
                $f = isset($found['odds_factor']) && is_numeric($found['odds_factor']) ? (int) $found['odds_factor'] : $s;
                if ($f < 1) {
                    $f = 1;
                }
                $normalized[] = [
                    'streak'      => $s,
                    'odds_factor' => $f,
                    'is_jackpot'  => !empty($found['is_jackpot']),
                ];
            }
        }
        $json = json_encode(['rows' => $normalized], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($json === false) {
            return '{"rows":[]}';
        }

        return $json;
    }
}
