<?php

/**
 * 文件功能：AI小班长自动参与百家乐下注
 *
 * 在每局百家乐开启时延迟调度执行：
 * 1. 检查是否存在连输休息惩罚（1小时）
 * 2. 检查可用金币与活动时间窗，确定本局下注额度
 * 3. 调用 AI 接口预测路单走势决定下注方向（AI 不可用时回退本地决策）
 * 4. 提交下注
 *
 * @author   ChatRoom Laravel
 *
 * @version  1.0.0
 */

namespace App\Jobs;

use App\Enums\CurrencySource;
use App\Events\MessageSent;
use App\Models\BaccaratBet;
use App\Models\BaccaratRound;
use App\Models\GameConfig;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\AiFinanceService;
use App\Services\BaccaratLossCoverService;
use App\Services\BaccaratPredictionService;
use App\Services\ChatStateService;
use App\Services\UserCurrencyService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

/**
 * 控制 AI小班长在百家乐中的下注、观望与资金调度行为。
 */
class AiBaccaratBetJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * 注入当前需要处理的百家乐局次。
     */
    public function __construct(
        public readonly BaccaratRound $round,
    ) {}

    /**
     * 执行 AI小班长本局百家乐的完整决策流程。
     */
    public function handle(UserCurrencyService $currency, AiFinanceService $aiFinance): void
    {
        // 1. 检查总开关与游戏开关
        if (Sysparam::getValue('chatbot_enabled', '0') !== '1' || ! GameConfig::isEnabled('baccarat')) {
            return;
        }

        $user = User::where('username', 'AI小班长')->first();
        if (! $user) {
            return;
        }

        // 2. 资金管理：自动存款与领取补偿
        $this->manageFinances($user, $aiFinance);

        $round = $this->round->fresh();
        if (! $round || ! $round->isBettingOpen()) {
            return;
        }

        // 3. 检查连输惩罚超时
        if (Redis::exists('ai_baccarat_timeout')) {
            return; // 还在禁赛期
        }

        // 4. 检查余额与限额
        $config = GameConfig::forGame('baccarat')?->params ?? [];
        $minBet = (int) ($config['min_bet'] ?? 100);
        $maxBet = (int) ($config['max_bet'] ?? 50000);

        // 5. 查询当前是否命中“你玩游戏我买单”活动窗口。
        $lossCoverService = app(BaccaratLossCoverService::class);
        $lossCoverEvent = $lossCoverService->findEventForBetTime(now());
        $isInLossCoverWindow = $lossCoverEvent !== null;

        // 买单活动进行中时允许 AI 统筹“手上 + 银行”总资产；平时只动用当前手上的 jjb。
        $bettableGold = $isInLossCoverWindow
            ? $aiFinance->getTotalGoldAssets($user)
            : $aiFinance->getSpendableGold($user);
        if ($bettableGold < $minBet) {
            return; // 资金不足以支撑最小下注
        }

        // 6. 获取近期路单和 AI 历史下注
        $recentResults = BaccaratRound::query()
            ->where('status', 'settled')
            ->orderByDesc('id')
            ->limit(20)
            ->pluck('result')
            ->toArray();

        $historicalBets = BaccaratBet::query()
            ->join('baccarat_rounds', 'baccarat_bets.round_id', '=', 'baccarat_rounds.id')
            ->where('baccarat_bets.user_id', $user->id)
            ->where('baccarat_rounds.status', 'settled')
            ->orderByDesc('baccarat_rounds.id')
            ->limit(10)
            ->select('baccarat_bets.*')
            ->get()
            ->map(function ($bet) {
                return [
                    'round_id' => $bet->round_id,
                    'bet_type' => $bet->bet_type,
                    'amount' => $bet->amount,
                    'profit' => $bet->profit ?? 0,
                ];
            })
            ->toArray();

        // 7. 调用 AI 接口出具统筹策略
        $predictionService = app(BaccaratPredictionService::class);
        $context = [
            'recent_results' => $recentResults,
            'available_gold' => $bettableGold,
            'historical_bets' => $historicalBets,
            'loss_cover_active' => $isInLossCoverWindow,
        ];

        $aiPrediction = $predictionService->predict($context);
        $decisionSource = 'ai';

        $betType = 'pass';
        $amount = 0;
        $reason = '';

        if ($aiPrediction) {
            $betType = $aiPrediction['action'];
            $percent = $aiPrediction['percentage'];
            $reason = $aiPrediction['reason'];

            // 买单活动期间不允许 AI 选择观望，避免错过“输也可返还”的活动资格。
            if ($isInLossCoverWindow && $betType === 'pass') {
                $decisionSource = 'local';
                $betType = $this->resolveLocalBetType($recentResults, false);
                $reason = trim($reason.' 买单活动进行中，本局禁止观望，已切换本地强制参战策略。');
            }

            if ($betType !== 'pass') {
                if ($isInLossCoverWindow) {
                    // 买单活动进行中且金币足够时，直接按百家乐单局最高限额下注。
                    $amount = min($bettableGold, $maxBet);
                    $reason = trim($reason.' 买单活动进行中，本局按百家乐最高限额下注。');
                } else {
                    // 非买单活动期间，限定单局最高下注不超过手头金币的 5% 以防止 AI 破产。
                    $percent = min(5, max(0, $percent));
                    $amount = (int) round($bettableGold * ($percent / 100.0));
                    $amount = max($minBet, min($amount, $maxBet));
                    if ($amount > $bettableGold) {
                        $amount = $bettableGold;
                    }
                }
            }
        } else {
            // AI 不可用时回退本地路单决策（保底逻辑）
            $decisionSource = 'local';
            // 买单活动期间，本地兜底策略同样不能返回观望。
            $betType = $this->resolveLocalBetType($recentResults, ! $isInLossCoverWindow);

            if ($betType !== 'pass') {
                if ($isInLossCoverWindow) {
                    // 本地兜底策略命中买单活动时，同样优先按百家乐最高限额下注。
                    $amount = min($bettableGold, $maxBet);
                    $reason = '买单活动进行中，采用本地最高限额下注兜底策略。';
                } else {
                    $percent = rand(2, 5) / 100.0;
                    $amount = (int) round($bettableGold * $percent);
                    $amount = max($minBet, min($amount, $maxBet));
                }
            }
        }

        // 记录 AI 小班长本次决策日志
        $labelMap = ['big' => '大', 'small' => '小', 'triple' => '豹子', 'pass' => '观望'];
        Log::channel('daily')->info('AI小班长百家乐决策', [
            'round_id' => $round->id,
            'decision_source' => $decisionSource === 'ai' ? 'AI接口策略' : '本地路单兜底',
            'action' => $labelMap[$betType] ?? $betType,
            'bet_amount' => $amount,
            'reason' => $reason,
            'roadmap_20' => implode('→', array_map(fn (string $r) => $labelMap[$r] ?? $r, array_reverse($recentResults))),
        ]);

        if ($betType === 'pass') {
            // 观望时仅广播，不执行真正的金币扣除下注逻辑
            $this->broadcastPassMessage($user, $round->room_id ?? 1, $reason);

            return;
        }

        // 买单活动期间允许为本次高额下注从银行调拨；非活动期间只检查当前手上金币是否够本次下注。
        $isReadyToSpend = $isInLossCoverWindow
            ? $aiFinance->prepareAllInSpend($user, $amount)
            : $aiFinance->prepareSpend($user, $amount);
        if (! $isReadyToSpend) {
            return;
        }

        // 二次校验，防止大模型接口调用太慢导致下注时该局已关闭
        $round->refresh();
        if (! $round->isBettingOpen()) {
            Log::channel('daily')->warning("AI小班长百家乐下注拦截：由于AI接口思考耗时，这局#{$round->id}投注已截止。");

            return;
        }

        // 8. 执行下注 (同 BaccaratController::bet 逻辑)
        DB::transaction(function () use ($user, $round, $betType, $amount, $currency, $lossCoverEvent, $lossCoverService) {
            $lockedUser = User::query()->lockForUpdate()->find($user->id);
            if (! $lockedUser || (int) ($lockedUser->jjb ?? 0) < $amount) {
                return;
            }

            // 幂等：同一局只能下一注
            $existing = BaccaratBet::query()
                ->where('round_id', $round->id)
                ->where('user_id', $lockedUser->id)
                ->lockForUpdate()
                ->exists();

            if ($existing) {
                return;
            }

            // 扣除金币
            $currency->change(
                $lockedUser,
                'gold',
                -$amount,
                CurrencySource::BACCARAT_BET,
                "AI小班长百家乐 #{$round->id} 押 ".match ($betType) {
                    'big' => '大', 'small' => '小', default => '豹子'
                },
            );

            // 写入下注记录
            $bet = BaccaratBet::create([
                'round_id' => $round->id,
                'user_id' => $lockedUser->id,
                'loss_cover_event_id' => $lossCoverEvent?->id,
                'bet_type' => $betType,
                'amount' => $amount,
                'status' => 'pending',
            ]);

            // 命中买单活动的下注需要登记到活动聚合记录里，确保后续能正确补偿返还。
            if ($lossCoverEvent) {
                $lossCoverService->registerBet($bet);
            }

            // 更新局次汇总统计
            $field = 'total_bet_'.$betType;
            $countField = 'bet_count_'.$betType;
            $round->increment($field, $amount);
            $round->increment($countField);
            $round->increment('bet_count');

            // 广播各选项的最新押注人数（让前台看到 AI 下注的人数增长）
            event(new \App\Events\BaccaratPoolUpdated($round));
        });

        // 下注完成后，若手上金币仍高于 100 万，则把超出的部分继续回存银行。
        $aiFinance->bankExcessGold($user);

        // 下注成功后，在聊天室发送一条普通聊天消息告知大家
        $this->broadcastBetMessage($user, $round->room_id ?? 1, $betType, $amount, $decisionSource);
    }

    /**
     * 广播 AI小班长的观望文案到聊天室
     *
     * @param  User  $user  AI小班长用户
     * @param  int  $roomId  聊天室 ID
     * @param  string  $reason  观望理由
     */
    private function broadcastPassMessage(User $user, int $roomId, string $reason): void
    {
        if (empty($reason)) {
            $reason = '风大雨大，保本最大，这把我决定观望一下！';
        }

        $chatState = app(ChatStateService::class);
        $content = "🌟 🎲 【百家乐】 <b>{$user->username}</b> 本局选择挂机观望：✨ <br/><span style='color:#666;'>[🤖 策略分析] {$reason}</span>";

        $msg = [
            'id' => $chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统传音',
            'to_user' => '大家',
            'content' => $content,
            'is_secret' => false,
            'font_color' => '#d97706',
            'action' => '',
            'sent_at' => now()->toDateTimeString(),
        ];

        $chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);
    }

    /**
     * 广播 AI小班长本次下注情况到聊天室
     *
     * 以普通聊天消息形式发送，发送对象为大家。
     *
     * @param  User  $user  AI小班长用户对象
     * @param  int  $roomId  目标房间 ID
     * @param  string  $betType  下注方向：big|small|triple
     * @param  int  $amount  下注金额
     * @param  string  $decisionSource  决策来源：ai | local
     */
    private function broadcastBetMessage(
        User $user,
        int $roomId,
        string $betType,
        int $amount,
        string $decisionSource,
    ): void {
        $chatState = app(ChatStateService::class);
        $labelMap = ['big' => '大', 'small' => '小', 'triple' => '豹子'];
        $label = $labelMap[$betType] ?? $betType;

        $sourceText = $decisionSource === 'ai' ? '🤖 经过深度算法预测，本局我看好：' : '📊 观察了下最近的路单，这把我觉得是：';
        $content = "🌟 🎲 【百家乐】 <b>{$user->username}</b> 已下注：<span style='color:#1d4ed8;font-weight:bold;'>{$label}</span> (".number_format($amount)." 金币)<br/><span style='color:#666;'>{$sourceText} {$label}！</span>";

        $msg = [
            'id' => $chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统传音',
            'to_user' => '大家',
            'content' => $content,
            'is_secret' => false,
            'font_color' => '#8b5cf6',
            'action' => '动态播报',
            'sent_at' => now()->toDateTimeString(),
        ];
        $chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);
    }

    /**
     * 生成本地路单兜底下注方向。
     *
     * @param  array<int, string>  $recentResults  最近已结算局次结果
     * @param  bool  $allowPass  是否允许返回观望
     */
    private function resolveLocalBetType(array $recentResults, bool $allowPass): string
    {
        $bigCount = count(array_filter($recentResults, fn (string $result) => $result === 'big'));
        $smallCount = count(array_filter($recentResults, fn (string $result) => $result === 'small'));

        // 默认保留少量押豹子概率，维持 AI 小班长原本的下注风格。
        if (rand(1, 100) <= 10) {
            return 'triple';
        }

        if ($bigCount > $smallCount) {
            return rand(1, 100) <= 70 ? 'big' : 'small';
        }

        if ($smallCount > $bigCount) {
            return rand(1, 100) <= 70 ? 'small' : 'big';
        }

        // 只有非买单活动时才允许空仓；活动期间必须至少押大或押小。
        if ($allowPass && rand(0, 10) === 0) {
            return 'pass';
        }

        return rand(0, 1) === 0 ? 'big' : 'small';
    }

    /**
     * AI 资金管理逻辑：优先领取补偿，再按“超过 100 万才存款”的规则整理持仓。
     */
    private function manageFinances(User $user, AiFinanceService $aiFinance): void
    {
        // 1. 检查是否有“买单”活动补偿可领取 (jjb 较低时优先领取)
        $lossCoverService = app(\App\Services\BaccaratLossCoverService::class);
        $pendingEvents = \App\Models\BaccaratLossCoverEvent::where('status', 'claimable')->get();
        foreach ($pendingEvents as $event) {
            $record = \App\Models\BaccaratLossCoverRecord::where('event_id', $event->id)
                ->where('user_id', $user->id)
                ->where('claim_status', 'pending')
                ->first();

            if ($record && $record->compensation_amount > 0) {
                $lossCoverService->claim($event, $user);
                Log::info("AI小班长自动领取活动补偿: Event #{$event->id}, Amount: {$record->compensation_amount}");
            }
        }

        $aiFinance->rebalanceHoldings($user);
    }
}
