<?php

/**
 * 文件功能：五子棋对战前台控制器
 *
 * 提供 PvP（随机对战）和 PvE（人机对战）两种模式的完整 API：
 * - 创建对局（支持两种模式）
 * - 加入 PvP 对战
 * - 落子（自动触发 AI 回应）
 * - 认输
 * - 取消邀请
 * - 获取对局状态
 *
 * @author  ChatRoom Laravel
 *
 * @version 1.0.0
 */

namespace App\Http\Controllers;

use App\Enums\CurrencySource;
use App\Events\GomokuFinishedEvent;
use App\Events\GomokuInviteEvent;
use App\Events\GomokuMovedEvent;
use App\Models\GameConfig;
use App\Models\GomokuGame;
use App\Services\GomokuAiService;
use App\Services\UserCurrencyService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class GomokuController extends Controller
{
    public function __construct(
        private readonly GomokuAiService $ai,
        private readonly UserCurrencyService $currency,
    ) {}

    /**
     * 创建对局。
     *
     * 支持两种模式：
     * - pvp: 广播邀请通知，等待其他玩家加入
     * - pve: 立即开局与 AI 对战（需支付入场费）
     */
    public function create(Request $request): JsonResponse
    {
        if (! GameConfig::isEnabled('gomoku')) {
            return response()->json(['ok' => false, 'message' => '五子棋当前未开启。']);
        }

        $data = $request->validate([
            'mode' => 'required|in:pvp,pve',
            'room_id' => 'required|integer|exists:rooms,id',
            'ai_level' => 'required_if:mode,pve|nullable|integer|min:1|max:4',
        ]);

        $user = $request->user();

        // PvP：检查是否已在等待/对局中（一次只能参与一场）
        $activeGame = GomokuGame::query()
            ->where(function ($q) use ($user) {
                $q->where('player_black_id', $user->id)
                    ->orWhere('player_white_id', $user->id);
            })
            ->whereIn('status', ['waiting', 'playing'])
            ->first();

        if ($activeGame) {
            return response()->json(['ok' => false, 'message' => '您当前已有进行中的对局，请先完成或取消。']);
        }

        // PvE：扣除入场费
        $entryFee = 0;
        if ($data['mode'] === 'pve') {
            $entryFee = $this->getPveEntryFee((int) $data['ai_level']);
            if ($entryFee > 0 && ($user->jjb ?? 0) < $entryFee) {
                return response()->json(['ok' => false, 'message' => "金币不足，此难度需 {$entryFee} 金币入场费。"]);
            }
        }

        return DB::transaction(function () use ($user, $data, $entryFee): JsonResponse {
            // PvE 扣除入场费
            if ($entryFee > 0) {
                $this->currency->change(
                    $user,
                    'gold',
                    -$entryFee,
                    CurrencySource::GOMOKU_ENTRY_FEE,
                    "五子棋 AI 对战入场费（难度{$data['ai_level']}）",
                );
            }

            $timeout = (int) GameConfig::param('gomoku', 'invite_timeout', 60);

            $game = GomokuGame::create([
                'mode' => $data['mode'],
                'room_id' => $data['room_id'],
                'player_black_id' => $user->id,
                'ai_level' => $data['mode'] === 'pve' ? ($data['ai_level'] ?? 1) : null,
                'status' => $data['mode'] === 'pve' ? 'playing' : 'waiting',
                'board' => GomokuGame::emptyBoard(),
                'current_turn' => 1,
                'entry_fee' => $entryFee,
                'invite_expires_at' => $data['mode'] === 'pvp' ? now()->addSeconds($timeout) : null,
                'started_at' => $data['mode'] === 'pve' ? now() : null,
            ]);

            // PvP：广播邀请通知至房间
            if ($data['mode'] === 'pvp') {
                broadcast(new GomokuInviteEvent($game, $user->username));
            }

            return response()->json([
                'ok' => true,
                'game_id' => $game->id,
                'message' => $data['mode'] === 'pvp'
                    ? '已发起对战邀请，等待其他玩家加入…'
                    : '对局已开始，您执黑棋先手！',
            ]);
        });
    }

    /**
     * 加入 PvP 对战（白棋方）。
     */
    public function join(Request $request, GomokuGame $game): JsonResponse
    {
        $user = $request->user();

        if ($game->status !== 'waiting') {
            return response()->json(['ok' => false, 'message' => '该对局已不在等待状态。']);
        }

        if ($game->player_black_id === $user->id) {
            return response()->json(['ok' => false, 'message' => '不能加入自己发起的对局。']);
        }

        if ($game->invite_expires_at && now()->isAfter($game->invite_expires_at)) {
            $game->update(['status' => 'cancelled']);

            return response()->json(['ok' => false, 'message' => '该邀请已超时，请重新发起。']);
        }

        // 检查接受方是否已在其他对局中
        $activeGame = GomokuGame::query()
            ->where(function ($q) use ($user) {
                $q->where('player_black_id', $user->id)
                    ->orWhere('player_white_id', $user->id);
            })
            ->whereIn('status', ['waiting', 'playing'])
            ->first();

        if ($activeGame) {
            return response()->json(['ok' => false, 'message' => '您当前已有进行中的对局。']);
        }

        $game->update([
            'player_white_id' => $user->id,
            'status' => 'playing',
            'started_at' => now(),
        ]);

        return response()->json([
            'ok' => true,
            'game_id' => $game->id,
            'message' => '已成功加入对战！您执白棋。',
        ]);
    }

    /**
     * 落子。
     *
     * PvP 模式：验证轮次后广播落子。
     * PvE 模式：玩家落子后，自动计算 AI 落点并一并返回。
     */
    public function move(Request $request, GomokuGame $game): JsonResponse
    {
        $user = $request->user();

        if ($game->status !== 'playing') {
            return response()->json(['ok' => false, 'message' => '对局未在进行中。']);
        }

        $data = $request->validate([
            'row' => 'required|integer|min:0|max:14',
            'col' => 'required|integer|min:0|max:14',
        ]);

        $row = (int) $data['row'];
        $col = (int) $data['col'];
        $board = $game->board;

        // 坐标已被占用
        if (GomokuGame::isOccupied($board, $row, $col)) {
            return response()->json(['ok' => false, 'message' => '该位置已有棋子。']);
        }

        // PvP：验证是否轮到该玩家
        if ($game->mode === 'pvp') {
            if (! $game->belongsToUser($user->id)) {
                return response()->json(['ok' => false, 'message' => '您不在该对局中。']);
            }
            if (! $game->isUserTurn($user->id)) {
                return response()->json(['ok' => false, 'message' => '当前不是您的回合。']);
            }
        } else {
            // PvE：只允许黑棋玩家操作
            if ($game->player_black_id !== $user->id) {
                return response()->json(['ok' => false, 'message' => '您不在该对局中。']);
            }
            if ($game->current_turn !== 1) {
                return response()->json(['ok' => false, 'message' => 'AI 正在思考，请等待。']);
            }
        }

        return DB::transaction(function () use ($game, $row, $col, $board, $user): JsonResponse {
            // 玩家落子
            $playerColor = $game->mode === 'pvp' ? $game->colorOf($user->id) : 1;
            $board = GomokuGame::placeStone($board, $row, $col, $playerColor);

            // 记录落子历史
            $history = $game->moves_history ?? [];
            $history[] = ['row' => $row, 'col' => $col, 'color' => $playerColor, 'at' => now()->toIso8601String()];

            // 判断玩家是否胜利
            if (GomokuGame::checkWin($board, $row, $col, $playerColor)) {
                return $this->finishGame($game, $board, $history, $playerColor, 'win', $user);
            }

            // 判断平局
            if (GomokuGame::isBoardFull($board)) {
                return $this->finishGame($game, $board, $history, 0, 'draw', $user);
            }

            // 切换回合
            $nextTurn = $playerColor === 1 ? 2 : 1;
            $game->update([
                'board' => $board,
                'current_turn' => $nextTurn,
                'moves_history' => $history,
            ]);

            // PvP：广播落子事件
            if ($game->mode === 'pvp') {
                broadcast(new GomokuMovedEvent($game->fresh(), $row, $col, $playerColor));

                return response()->json(['ok' => true, 'moved' => compact('row', 'col')]);
            }

            // PvE：AI 落子
            $aiMove = $this->ai->think($board, $game->ai_level ?? 1);
            $aiRow = $aiMove['row'];
            $aiCol = $aiMove['col'];
            $aiColor = 2;
            $board = GomokuGame::placeStone($board, $aiRow, $aiCol, $aiColor);

            $history[] = ['row' => $aiRow, 'col' => $aiCol, 'color' => $aiColor, 'at' => now()->toIso8601String()];

            // 判断 AI 是否胜利
            if (GomokuGame::checkWin($board, $aiRow, $aiCol, $aiColor)) {
                return $this->finishGame($game, $board, $history, $aiColor, 'win', $user);
            }

            // 再次检查平局（AI 落子后）
            if (GomokuGame::isBoardFull($board)) {
                return $this->finishGame($game, $board, $history, 0, 'draw', $user);
            }

            // AI 落子后切换回玩家回合
            $game->update([
                'board' => $board,
                'current_turn' => 1,
                'moves_history' => $history,
            ]);

            return response()->json([
                'ok' => true,
                'moved' => ['row' => $row, 'col' => $col],
                'ai_moved' => ['row' => $aiRow, 'col' => $aiCol],
            ]);
        });
    }

    /**
     * 认输（当前玩家主动认输，对手获胜）。
     */
    public function resign(Request $request, GomokuGame $game): JsonResponse
    {
        $user = $request->user();

        if (! in_array($game->status, ['playing', 'waiting'])) {
            return response()->json(['ok' => false, 'message' => '对局已结束。']);
        }

        if (! $game->belongsToUser($user->id)) {
            return response()->json(['ok' => false, 'message' => '您不在该对局中。']);
        }

        // 认输者对应颜色，胜方为另一色
        $resignColor = $game->colorOf($user->id);
        $winnerColor = $resignColor === 1 ? 2 : 1;

        return DB::transaction(function () use ($game, $winnerColor, $user): JsonResponse {
            return $this->finishGame($game, $game->board, $game->moves_history ?? [], $winnerColor, 'resign', $user);
        });
    }

    /**
     * 取消 PvP 邀请（发起者主动取消，或超时后被调用）。
     */
    public function cancel(Request $request, GomokuGame $game): JsonResponse
    {
        $user = $request->user();

        if ($game->status !== 'waiting') {
            return response()->json(['ok' => false, 'message' => '该对局已不在等待状态。']);
        }

        if ($game->player_black_id !== $user->id) {
            return response()->json(['ok' => false, 'message' => '只有发起者可取消邀请。']);
        }

        $game->update(['status' => 'cancelled']);

        return response()->json(['ok' => true, 'message' => '邀请已取消。']);
    }

    /**
     * 获取对局当前状态（用于前端重连同步）。
     */
    public function state(Request $request, GomokuGame $game): JsonResponse
    {
        $user = $request->user();

        if (! $game->belongsToUser($user->id) && $game->mode === 'pvp') {
            return response()->json(['ok' => false, 'message' => '无权访问该对局。']);
        }

        return response()->json([
            'ok' => true,
            'game_id' => $game->id,
            'mode' => $game->mode,
            'status' => $game->status,
            'board' => $game->board,
            'current_turn' => $game->current_turn,
            'winner' => $game->winner,
            'your_color' => $game->colorOf($user->id),
            'ai_level' => $game->ai_level,
            'reward_gold' => $game->reward_gold,
        ]);
    }

    // ─── 私有工具方法 ─────────────────────────────────────────────────

    /**
     * 结算对局：更新状态、发放奖励、广播事件。
     *
     * @param  GomokuGame  $game  当前对局
     * @param  array  $board  最终棋盘
     * @param  array  $history  落子历史
     * @param  int  $winnerColor  胜方颜色（0=平局）
     * @param  string  $reason  结束原因（win/draw/resign）
     * @param  \App\Models\User  $currentUser  当前操作用户（用于加载用户名）
     */
    private function finishGame(
        GomokuGame $game,
        array $board,
        array $history,
        int $winnerColor,
        string $reason,
        mixed $currentUser
    ): JsonResponse {
        $rewardGold = 0;
        $winnerName = '';
        $loserName = '';

        // 加载对局玩家信息
        $game->load('playerBlack', 'playerWhite');

        if ($winnerColor === 0) {
            // 平局
            $winnerName = '';
            $loserName = '';

            // PvE 平局：返还入场费
            if ($game->mode === 'pve' && $game->entry_fee > 0) {
                $this->currency->change(
                    $game->playerBlack,
                    'gold',
                    $game->entry_fee,
                    CurrencySource::GOMOKU_REFUND,
                    '五子棋 AI 平局返还入场费',
                );
            }
        } else {
            // 有胜负
            $rewardGold = $this->calculateReward($game, $winnerColor);

            if ($game->mode === 'pvp') {
                $winnerUser = $winnerColor === 1 ? $game->playerBlack : $game->playerWhite;
                $loserUser = $winnerColor === 1 ? $game->playerWhite : $game->playerBlack;
                $winnerName = $winnerUser?->username ?? '';
                $loserName = $loserUser?->username ?? '';

                // 将英文 reason 转为友好的中文后缀
                $reasonText = match ($reason) {
                    'resign' => '（认输）',
                    'timeout' => '（超时）',
                    default => '',
                };

                // 发放 PvP 胜利奖励给获胜玩家
                if ($winnerUser && $rewardGold > 0) {
                    $this->currency->change(
                        $winnerUser,
                        'gold',
                        $rewardGold,
                        CurrencySource::GOMOKU_WIN,
                        "五子棋：击败 {$loserName}{$reasonText}",
                    );
                }
            } else {
                // PvE 模式：winnerColor=1 代表玩家胜
                if ($winnerColor === 1) {
                    $winnerName = $game->playerBlack->username ?? '';
                    $loserName = "AI（难度{$game->ai_level}）";

                    if ($rewardGold > 0) {
                        $this->currency->change(
                            $game->playerBlack,
                            'gold',
                            $rewardGold,
                            CurrencySource::GOMOKU_WIN,
                            "五子棋：击败 AI（难度{$game->ai_level}）",
                        );
                    }
                } else {
                    // AI 获胜：入场费已扣，无返还
                    $winnerName = "AI（难度{$game->ai_level}）";
                    $loserName = $game->playerBlack->username ?? '';
                }
            }
        }

        $game->update([
            'status' => 'finished',
            'board' => $board,
            'moves_history' => $history,
            'winner' => $winnerColor,
            'reward_gold' => $rewardGold,
            'finished_at' => now(),
        ]);

        // 广播对局结束事件给参与对局的双方
        broadcast(new GomokuFinishedEvent($game->fresh(), $winnerName, $loserName, $reason));

        // 有胜负，均向房间广播系统通知
        if ($winnerColor !== 0) {
            if ($game->mode === 'pvp') {
                // PvP：胜方玩家获奖通知
                $reasonText = match ($reason) {
                    'resign' => '（认输）',
                    default => '',
                };
                $text = "♟️ 【五子棋】玩家对战结果！恭喜玩家【{$winnerName}】击败了【{$loserName}】{$reasonText}，赢得 {$rewardGold} 金币！";
            } elseif ($winnerColor === 1) {
                // PvE：玩家获胜
                $text = "♟️ 【五子棋】棋神降临！恭喜玩家【{$winnerName}】在人机对战（难度{$game->ai_level}）中击败 AI，赢得 {$rewardGold} 金币！";
            } else {
                // PvE：AI 获胜（玩家输了）
                $text = "♟️ 【五子棋】AI 大获全胜！玩家【{$loserName}】在人机对战（难度{$game->ai_level}）中不敌 AI，再接再厉！";
            }
            $this->broadcastSystemMessage($game->room_id, $text);
        }

        return response()->json([
            'ok' => true,
            'finished' => true,
            'winner' => $winnerColor,
            'winner_name' => $winnerName,
            'reason' => $reason,
            'reward_gold' => $rewardGold,
        ]);
    }

    /**
     * 发送系统房间广播。
     *
     * @param  int  $roomId  房间ID
     * @param  string  $content  广播内容
     */
    private function broadcastSystemMessage(int $roomId, string $content): void
    {
        $chatState = app(\App\Services\ChatStateService::class);
        $messageData = [
            '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, $messageData);
        broadcast(new \App\Events\MessageSent($roomId, $messageData));
        \App\Jobs\SaveMessageJob::dispatch($messageData);
    }

    /**
     * 根据对局模式和获胜方计算奖励金币。
     *
     * @param  GomokuGame  $game  对局
     * @param  int  $winnerColor  胜方颜色
     */
    private function calculateReward(GomokuGame $game, int $winnerColor): int
    {
        if ($game->mode === 'pvp') {
            // PvP 胜利奖励从游戏配置读取
            return (int) GameConfig::param('gomoku', 'pvp_reward', 80);
        }

        // PvE：AI 胜利无奖励
        if ($winnerColor !== 1) {
            return 0;
        }

        // 按难度从游戏配置读取胜利奖励
        $key = match ((int) $game->ai_level) {
            1 => 'pve_easy_reward',
            2 => 'pve_normal_reward',
            3 => 'pve_hard_reward',
            default => 'pve_expert_reward',
        };

        $defaults = ['pve_easy_reward' => 20, 'pve_normal_reward' => 50, 'pve_hard_reward' => 120, 'pve_expert_reward' => 300];

        return (int) GameConfig::param('gomoku', $key, $defaults[$key]);
    }

    /**
     * 根据 AI 难度获取 PvE 入场费。
     *
     * @param  int  $aiLevel  AI 难度（1-4）
     */
    private function getPveEntryFee(int $aiLevel): int
    {
        // 从游戏配置读取各难度入场费，支持后台实时调整
        $key = match ($aiLevel) {
            1 => 'pve_easy_fee',
            2 => 'pve_normal_fee',
            3 => 'pve_hard_fee',
            default => 'pve_expert_fee',
        };

        $defaults = ['pve_easy_fee' => 0, 'pve_normal_fee' => 10, 'pve_hard_fee' => 30, 'pve_expert_fee' => 80];

        return (int) GameConfig::param('gomoku', $key, $defaults[$key]);
    }

    /**
     * 查询当前用户是否有进行中的对局（重进页面时用于恢复）。
     *
     * 返回对局基础信息，包含模式、棋盘状态与双方用户名，
     * 让前端弹出「继续 / 认输」选择。
     */
    public function active(Request $request): JsonResponse
    {
        $user = $request->user();

        $game = GomokuGame::query()
            ->where(function ($q) use ($user) {
                $q->where('player_black_id', $user->id)
                    ->orWhere('player_white_id', $user->id);
            })
            ->whereIn('status', ['waiting', 'playing'])
            ->with('playerBlack', 'playerWhite')
            ->latest()
            ->first();

        if (! $game) {
            return response()->json(['ok' => true, 'has_active' => false]);
        }

        // 对阵双方用户名
        $blackName = $game->playerBlack->username ?? '黑棋';
        $whiteName = $game->mode === 'pve'
            ? ('AI（难度'.$game->ai_level.'）')
            : ($game->playerWhite?->username ?? '等待中…');

        return response()->json([
            'ok' => true,
            'has_active' => true,
            'game_id' => $game->id,
            'mode' => $game->mode,
            'status' => $game->status,
            'ai_level' => $game->ai_level,
            'your_color' => $game->colorOf($user->id),
            'board' => $game->board,
            'current_turn' => $game->current_turn,
            'black_name' => $blackName,
            'white_name' => $whiteName,
        ]);
    }
}
