<?php
declare(strict_types=1);

namespace app\api\service;

use app\dice\model\lottery_pool_config\DiceLotteryPoolConfig;
use app\dice\model\player\DicePlayer;
use support\think\Cache;

/**
 * 彩金池实例，按玩家权重与奖池配置创建，存 Redis 便于增删改查
 */
class LotteryService
{
    private const REDIS_KEY_PREFIX = 'api:game:lottery_pool:';
    private const REDIS_KEY_START_INDEX = 'api:game:start_index:';
    private const EXPIRE = 86400 * 7; // 7天

    private int $playerId;
    private ?int $configType0Id = null;
    private ?int $configType1Id = null;
    /** @var array{t1_weight?:int,t2_weight?:int,t3_weight?:int,t4_weight?:int,t5_weight?:int} */
    private array $playerWeights = [];

    public function __construct(int $playerId)
    {
        $this->playerId = $playerId;
    }

    public static function getRedisKey(int $playerId): string
    {
        return self::REDIS_KEY_PREFIX . $playerId;
    }

    public static function getStartIndexKey(int $playerId): string
    {
        return self::REDIS_KEY_START_INDEX . $playerId;
    }

    /** 从 Redis 加载或根据玩家与 DiceLotteryPoolConfig 创建并保存 */
    public static function getOrCreate(int $playerId): self
    {
        $key = self::getRedisKey($playerId);
        $cached = Cache::get($key);
        if ($cached && is_string($cached)) {
            $data = json_decode($cached, true);
            if (is_array($data)) {
                $s = new self($playerId);
                $s->configType0Id = (int) ($data['config_type_0_id'] ?? 0);
                $s->configType1Id = (int) ($data['config_type_1_id'] ?? 0);
                $s->playerWeights = $data['player_weights'] ?? [];
                return $s;
            }
        }
        $player = DicePlayer::find($playerId);
        if (!$player) {
            throw new \RuntimeException('玩家不存在');
        }
        $config0 = DiceLotteryPoolConfig::where('name', 'default')->find();
        $config1 = DiceLotteryPoolConfig::where('name', 'killScore')->find();
        $s = new self($playerId);
        $s->configType0Id = $config0 ? (int) $config0->id : null;
        $s->configType1Id = $config1 ? (int) $config1->id : null;
        $s->playerWeights = [
            't1_weight' => (int) ($player->t1_weight ?? 0),
            't2_weight' => (int) ($player->t2_weight ?? 0),
            't3_weight' => (int) ($player->t3_weight ?? 0),
            't4_weight' => (int) ($player->t4_weight ?? 0),
            't5_weight' => (int) ($player->t5_weight ?? 0),
        ];
        $s->save();
        return $s;
    }

    public function save(): void
    {
        $key = self::getRedisKey($this->playerId);
        $data = [
            'config_type_0_id' => $this->configType0Id,
            'config_type_1_id' => $this->configType1Id,
            'player_weights'   => $this->playerWeights,
        ];
        Cache::set($key, json_encode($data), self::EXPIRE);
    }

    /**
     * 使玩家彩金池 Redis 快照失效（下次 getOrCreate 从库重建）
     */
    public static function invalidatePlayerLotteryCache(int $playerId): void
    {
        if ($playerId <= 0) {
            return;
        }
        Cache::delete(self::getRedisKey($playerId));
    }

    /**
     * 若 Redis 中已有该玩家彩金池快照，则仅更新 player_weights（避免全量重建）
     *
     * @param array{t1_weight?:int,t2_weight?:int,t3_weight?:int,t4_weight?:int,t5_weight?:int} $weights
     */
    public static function patchPlayerWeightsCache(int $playerId, array $weights): void
    {
        if ($playerId <= 0) {
            return;
        }
        $key = self::getRedisKey($playerId);
        $cached = Cache::get($key);
        if (!$cached || !is_string($cached)) {
            return;
        }
        $data = json_decode($cached, true);
        if (!is_array($data)) {
            Cache::delete($key);
            return;
        }
        $data['player_weights'] = [
            't1_weight' => (int) ($weights['t1_weight'] ?? 0),
            't2_weight' => (int) ($weights['t2_weight'] ?? 0),
            't3_weight' => (int) ($weights['t3_weight'] ?? 0),
            't4_weight' => (int) ($weights['t4_weight'] ?? 0),
            't5_weight' => (int) ($weights['t5_weight'] ?? 0),
        ];
        Cache::set($key, json_encode($data), self::EXPIRE);
    }

    /** 根据奖池配置的 t1_weight..t5_weight 权重随机抽取档位 T1-T5 */
    public static function drawTierByWeights(DiceLotteryPoolConfig $config): string
    {
        $tiers = ['T1', 'T2', 'T3', 'T4', 'T5'];
        $weights = [
            (int) ($config->t1_weight ?? 0),
            (int) ($config->t2_weight ?? 0),
            (int) ($config->t3_weight ?? 0),
            (int) ($config->t4_weight ?? 0),
            (int) ($config->t5_weight ?? 0),
        ];
        return self::drawTierByWeightArray($tiers, $weights);
    }

    /**
     * 根据玩家 t1_weight～t5_weight 权重随机抽取中奖档位 T1-T5
     * t1_weight=T1, t2_weight=T2, t3_weight=T3, t4_weight=T4, t5_weight=T5
     */
    public static function drawTierByPlayerWeights(DicePlayer $player): string
    {
        $tiers = ['T1', 'T2', 'T3', 'T4', 'T5'];
        $weights = [
            (int) ($player->t1_weight ?? 0),
            (int) ($player->t2_weight ?? 0),
            (int) ($player->t3_weight ?? 0),
            (int) ($player->t4_weight ?? 0),
            (int) ($player->t5_weight ?? 0),
        ];
        return self::drawTierByWeightArray($tiers, $weights);
    }

    /**
     * 根据 T1-T5 权重数组抽取档位（用于测试自定义档位概率）
     * @param array $tierWeights 如 ['T1'=>100, 'T2'=>200, ...] 或 [100,200,300,400,500]
     */
    public static function drawTierByWeightsFromArray(array $tierWeights): string
    {
        $tiers = ['T1', 'T2', 'T3', 'T4', 'T5'];
        $weights = [];
        foreach ($tiers as $i => $t) {
            $weights[] = (int) ($tierWeights[$t] ?? $tierWeights[$i] ?? 0);
        }
        return self::drawTierByWeightArray($tiers, $weights);
    }

    /** 按档位权重数组抽取 T1-T5 */
    private static function drawTierByWeightArray(array $tiers, array $weights): string
    {
        $total = array_sum($weights);
        if ($total <= 0) {
            return $tiers[random_int(0, count($tiers) - 1)];
        }
        $r = random_int(1, $total);
        $acc = 0;
        foreach ($weights as $i => $w) {
            $acc += $w;
            if ($r <= $acc) {
                return $tiers[$i];
            }
        }
        return $tiers[4];
    }

    /** 按 paid_ticket_count 与 free_ticket_count 权重随机抽取 0=付费 1=免费 */
    public static function drawTicketType(int $paid, int $free): int
    {
        if ($paid <= 0 && $free <= 0) {
            throw new \RuntimeException('抽奖券不足');
        }
        if ($paid <= 0) {
            return 1;
        }
        if ($free <= 0) {
            return 0;
        }
        $total = $paid + $free;
        $r = random_int(1, $total);
        return $r <= $paid ? 0 : 1;
    }

    public function getConfigType0Id(): ?int
    {
        return $this->configType0Id;
    }

    public function getConfigType1Id(): ?int
    {
        return $this->configType1Id;
    }
}
