<?php

/**
 * 文件功能：AI小班长专属极轻量心跳模拟器
 *
 * 专门用于让无法通过浏览器发送真实心跳的 AI实体用户
 * 也能够完美触原有的法发经验/金币逻辑以及触发随机事件（Autoact）。
 * 每分钟由 Laravel Scheduler 调用。
 *
 * @author   ChatRoom Laravel
 *
 * @version  1.0.0
 */

namespace App\Console\Commands;

use App\Enums\CurrencySource;
use App\Events\MessageSent;
use App\Jobs\SaveMessageJob;
use App\Models\Autoact;
use App\Models\DailySignIn;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\AiFinanceService;
use App\Services\ChatStateService;
use App\Services\SignInService;
use App\Services\UserCurrencyService;
use App\Services\VipService;
use Illuminate\Console\Command;

/**
 * 定时模拟 AI小班长心跳，并同步维护其常规存款理财行为。
 */
class AiHeartbeatCommand extends Command
{
    /**
     * Artisan 指令名称
     */
    protected $signature = 'chatroom:ai-heartbeat';

    /**
     * 指令描述
     */
    protected $description = '模拟 AI 小班长客户端心跳，触发经验金币与随机事件';

    /**
     * 注入聊天室状态、VIP、积分与 AI 资金调度服务。
     */
    public function __construct(
        private readonly ChatStateService $chatState,
        private readonly VipService $vipService,
        private readonly UserCurrencyService $currencyService,
        private readonly AiFinanceService $aiFinance,
        private readonly SignInService $signInService,
    ) {
        parent::__construct();
    }

    /**
     * 执行 AI小班长单次心跳，并处理奖励、随机事件与资金调度。
     */
    public function handle(): int
    {
        // 1. 检查总开关
        if (Sysparam::getValue('chatbot_enabled', '0') !== '1') {
            return Command::SUCCESS;
        }

        // 2. 获取 AI 实体
        $user = User::where('username', 'AI小班长')->first();
        if (! $user) {
            return Command::SUCCESS;
        }

        // 心跳开始前，若手上金币已高于 100 万，则先把超出的部分转入银行。
        $this->aiFinance->bankExcessGold($user);

        // 2.5 自动每日签到（今日已签时 claim() 幂等返回，不重复发奖）
        $this->performDailySignIn($user);

        // 3. 常规心跳经验与金币发放
        // (模拟前端每30-60秒发一次心跳的过程，此处每分钟跑一次，发放单人心跳奖励)
        $expGain = $this->parseRewardValue(Sysparam::getValue('exp_per_heartbeat', '1'));
        if ($expGain > 0) {
            $expMultiplier = $this->vipService->getExpMultiplier($user);
            $actualExpGain = (int) round($expGain * $expMultiplier);
            $user->exp_num += $actualExpGain;
        }

        $jjbGain = $this->parseRewardValue(Sysparam::getValue('jjb_per_heartbeat', '0'));
        if ($jjbGain > 0) {
            $jjbMultiplier = $this->vipService->getJjbMultiplier($user);
            $actualJjbGain = (int) round($jjbGain * $jjbMultiplier);
            $user->jjb = ($user->jjb ?? 0) + $actualJjbGain;
        }

        $user->save();
        $user->refresh();

        // 4. 重算等级（基础心跳升级）
        $superLevel = (int) Sysparam::getValue('superlevel', '100');
        $leveledUp = $this->calculateNewLevel($user, $superLevel);

        // 5. 随机事件触发
        $eventChance = (int) Sysparam::getValue('auto_event_chance', '10');
        if ($eventChance > 0 && rand(1, 100) <= $eventChance) {
            $autoEvent = Autoact::randomEvent();
            if ($autoEvent) {
                // 执行随机事件的金钱经验惩奖
                if ($autoEvent->exp_change !== 0) {
                    $this->currencyService->change(
                        $user, 'exp', $autoEvent->exp_change, CurrencySource::AUTO_EVENT, "随机事件：{$autoEvent->text_body}", 1
                    );
                }
                if ($autoEvent->jjb_change !== 0) {
                    $this->currencyService->change(
                        $user, 'gold', $autoEvent->jjb_change, CurrencySource::AUTO_EVENT, "随机事件：{$autoEvent->text_body}", 1
                    );
                }

                $user->refresh();

                // 重新计算等级
                if ($this->calculateNewLevel($user, $superLevel)) {
                    $leveledUp = true;
                }

                // 广播随机事件
                $this->broadcastSystemMessage(
                    '星海小博士',
                    $autoEvent->renderText($user->username),
                    match ($autoEvent->event_type) {
                        'good' => '#16a34a',
                        'bad' => '#dc2626',
                        default => '#7c3aed',
                    }
                );
            }
        }

        // 6. 如果由于心跳或事件导致了升级，广播升级消息
        if ($leveledUp) {
            $this->broadcastSystemMessage(
                '系统传音',
                "🌟 天道酬勤！恭喜侠客【{$user->username}】挂机苦修，境界突破至 LV.{$user->user_level}！",
                '#d97706',
                '大声宣告'
            );
        }

        // 7. 钓鱼小游戏随机参与逻辑
        $fishingEnabled = Sysparam::getValue('chatbot_fishing_enabled', '0') === '1';
        $fishingChance = (int) Sysparam::getValue('chatbot_fishing_chance', '100'); // 默认 5% 概率
        if ($fishingEnabled && $fishingChance > 0 && rand(1, 100) <= $fishingChance && \App\Models\GameConfig::isEnabled('fishing')) {
            $cost = (int) (\App\Models\GameConfig::param('fishing', 'fishing_cost') ?? Sysparam::getValue('fishing_cost', '5'));
            // 常规小游戏只使用当前手上金币，不再自动从银行补到 100 万。
            if ($this->aiFinance->prepareSpend($user, $cost)) {
                // 先扣除费用
                $this->currencyService->change(
                    $user, 'gold', -$cost,
                    CurrencySource::FISHING_COST,
                    "AI小班长钓鱼抛竿消耗 {$cost} 金币",
                    1,
                );

                // 模拟玩家等待时间
                $waitMin = (int) (\App\Models\GameConfig::param('fishing', 'fishing_wait_min') ?? Sysparam::getValue('fishing_wait_min', '8'));
                $waitMax = (int) (\App\Models\GameConfig::param('fishing', 'fishing_wait_max') ?? Sysparam::getValue('fishing_wait_max', '15'));
                $waitTime = rand($waitMin, $waitMax);

                // 延迟派发收竿事件（AI目前统一将事件播报到房间 1，或者拿 active room ids）
                $activeRoomIds = $this->chatState->getAllActiveRoomIds();
                $roomId = ! empty($activeRoomIds) ? $activeRoomIds[0] : 1;

                \App\Jobs\AiFishingJob::dispatch($user, $roomId)->delay(now()->addSeconds($waitTime));
            }
        }

        // 心跳结束后，若新增金币让手上金额超过 100 万，则把超出的部分重新转回银行。
        $this->aiFinance->bankExcessGold($user);

        return Command::SUCCESS;
    }

    /**
     * 计算并更新用户等级
     */
    private function calculateNewLevel(User $user, int $superLevel): bool
    {
        $oldLevel = $user->user_level;

        if ($oldLevel >= $superLevel) {
            return false; // 管理员不自动升降级
        }

        $newLevel = Sysparam::calculateLevel($user->exp_num);
        if ($newLevel !== $oldLevel && $newLevel < $superLevel) {
            $user->user_level = $newLevel;
            $user->save();

            return $newLevel > $oldLevel;
        }

        return false;
    }

    /**
     * 解析配置的奖励范围，如 "1" 或 "1-5"
     */
    private function parseRewardValue(string $raw): int
    {
        $raw = trim($raw);
        if (str_contains($raw, '-')) {
            [$min, $max] = explode('-', $raw, 2);

            return rand((int) $min, (int) $max);
        }

        return (int) $raw;
    }

    /**
     * 尝试为 AI小班长 执行今日签到，成功时广播签到通知。
     */
    private function performDailySignIn(User $user): void
    {
        // 先检查今日是否已签，避免每分钟都调用事务
        $alreadySigned = DailySignIn::query()
            ->where('user_id', $user->id)
            ->whereDate('sign_in_date', today())
            ->exists();

        if ($alreadySigned) {
            return;
        }

        // 获取活跃房间作为签到归属（默认房间 1）
        $activeRoomIds = $this->chatState->getAllActiveRoomIds();
        $roomId = ! empty($activeRoomIds) ? (int) $activeRoomIds[0] : 1;

        $dailySignIn = $this->signInService->claim($user, $roomId);

        // 仅当本次心跳实际完成签到时才广播（幂等保护）
        if (! $dailySignIn->wasRecentlyCreated) {
            return;
        }

        $rewardParts = [];
        if ($dailySignIn->gold_reward > 0) {
            $rewardParts[] = $dailySignIn->gold_reward.' 金币';
        }
        if ($dailySignIn->exp_reward > 0) {
            $rewardParts[] = $dailySignIn->exp_reward.' 经验';
        }
        if ($dailySignIn->charm_reward > 0) {
            $rewardParts[] = $dailySignIn->charm_reward.' 魅力';
        }
        $rewardText = $rewardParts === [] ? '签到记录' : implode(' + ', $rewardParts);

        $identityText = $dailySignIn->identity_badge_name
            ? '，获得身份 '.e($dailySignIn->identity_badge_name)
            : '';

        $content = '【'.e($user->username).'】完成今日签到，连续签到 '
            .$dailySignIn->streak_days.' 天，获得 '.$rewardText.$identityText.'。';

        $this->broadcastSystemMessage('签到播报', $content, '#0f766e');
    }

    /**
     * 往所有活跃房间发送系统广播消息
     */
    private function broadcastSystemMessage(string $fromUser, string $content, string $color, string $action = ''): void
    {
        $activeRoomIds = $this->chatState->getAllActiveRoomIds();
        if (empty($activeRoomIds)) {
            $activeRoomIds = [1];
        }

        foreach ($activeRoomIds as $roomId) {
            $sysMsg = [
                'id' => $this->chatState->nextMessageId($roomId),
                'room_id' => $roomId,
                'from_user' => $fromUser,
                'to_user' => '大家',
                'content' => $content,
                'is_secret' => false,
                'font_color' => $color,
                'action' => $action,
                'sent_at' => now()->toDateTimeString(),
            ];

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