<?php

/**
 * 文件功能：管理员聊天室实时命令控制器
 *
 * 提供管理员在聊天室内对用户执行的管理操作：
 * 警告(=J)、踢出(=T)、禁言(=B)、冻结(=Y)、查看私信(=S)、站长公屏讲话。
 *
 * 对应原 ASP 文件：DOUSER.ASP / KILLUSER.ASP / LOCKIP.ASP / NEWSAY.ASP
 *
 * @author   ChatRoom Laravel
 *
 * @version  1.0.0
 */

namespace App\Http\Controllers;

use App\Events\MessageSent;
use App\Jobs\SaveMessageJob;
use App\Models\Message;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\ChatStateService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redis;

class AdminCommandController extends Controller
{
    /**
     * 构造函数：注入聊天状态服务
     */
    public function __construct(
        private readonly ChatStateService $chatState,
    ) {}

    /**
     * 警告用户（=J 理由）
     *
     * 向目标用户发送系统警告消息，所有人可见。
     *
     * @param  Request  $request  请求对象，需包含 username, room_id, reason
     * @return JsonResponse 操作结果
     */
    public function warn(Request $request): JsonResponse
    {
        $request->validate([
            'username' => 'required|string',
            'room_id' => 'required|integer',
            'reason' => 'nullable|string|max:200',
        ]);

        $admin = Auth::user();
        $targetUsername = $request->input('username');
        $roomId = $request->input('room_id');
        $reason = $request->input('reason', '请注意言行');

        // 权限检查（等级由 level_warn 配置）
        if (! $this->canExecute($admin, $targetUsername, 'level_warn', '5')) {
            return response()->json(['status' => 'error', 'message' => '权限不足'], 403);
        }

        // 广播警告消息
        $msg = [
            'id' => $this->chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统传音',
            'to_user' => '大家',
            'content' => "⚠️ 管理员 <b>{$admin->username}</b> 警告 <b>{$targetUsername}</b>：{$reason}",
            'is_secret' => false,
            'font_color' => '#dc2626',
            'action' => '',
            'sent_at' => now()->toDateTimeString(),
        ];
        $this->chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);

        return response()->json(['status' => 'success', 'message' => "已警告 {$targetUsername}"]);
    }

    /**
     * 踢出用户（=T 理由）
     *
     * 将目标用户从聊天室踢出，清除其 Redis 在线状态。
     *
     * @param  Request  $request  请求对象，需包含 username, room_id, reason
     * @return JsonResponse 操作结果
     */
    public function kick(Request $request): JsonResponse
    {
        $request->validate([
            'username' => 'required|string',
            'room_id' => 'required|integer',
            'reason' => 'nullable|string|max:200',
        ]);

        $admin = Auth::user();
        $targetUsername = $request->input('username');
        $roomId = $request->input('room_id');
        $reason = $request->input('reason', '违反聊天室规则');

        // 权限检查（等级由 level_kick 配置）
        if (! $this->canExecute($admin, $targetUsername, 'level_kick', '10')) {
            return response()->json(['status' => 'error', 'message' => '权限不足'], 403);
        }

        // 从 Redis 在线列表移除
        $this->chatState->userLeave($roomId, $targetUsername);

        // 广播踢出消息（通知前端强制该用户跳转）
        $msg = [
            'id' => $this->chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统传音',
            'to_user' => '大家',
            'content' => "🚫 管理员 <b>{$admin->username}</b> 已将 <b>{$targetUsername}</b> 踢出聊天室。原因：{$reason}",
            'is_secret' => false,
            'font_color' => '#dc2626',
            'action' => '',
            'sent_at' => now()->toDateTimeString(),
        ];
        $this->chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);

        // 广播踢出事件（前端监听后强制跳转）
        broadcast(new \App\Events\UserKicked($roomId, $targetUsername, $reason));

        return response()->json(['status' => 'success', 'message' => "已踢出 {$targetUsername}"]);
    }

    /**
     * 禁言用户（=B 分钟数）
     *
     * 使用 Redis TTL 自动过期机制，禁止用户发言指定分钟数。
     *
     * @param  Request  $request  请求对象，需包含 username, room_id, duration
     * @return JsonResponse 操作结果
     */
    public function mute(Request $request): JsonResponse
    {
        $request->validate([
            'username' => 'required|string',
            'room_id' => 'required|integer',
            'duration' => 'required|integer|min:1|max:1440',
        ]);

        $admin = Auth::user();
        $targetUsername = $request->input('username');
        $roomId = $request->input('room_id');
        $duration = $request->input('duration');

        // 权限检查（等级由 level_mute 配置）
        if (! $this->canExecute($admin, $targetUsername, 'level_mute', '8')) {
            return response()->json(['status' => 'error', 'message' => '权限不足'], 403);
        }

        // 设置 Redis 禁言标记，TTL 自动过期
        $muteKey = "mute:{$roomId}:{$targetUsername}";
        Redis::setex($muteKey, $duration * 60, now()->toDateTimeString());

        // 广播禁言消息
        $msg = [
            'id' => $this->chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统传音',
            'to_user' => '大家',
            'content' => "🔇 管理员 <b>{$admin->username}</b> 已将 <b>{$targetUsername}</b> 禁言 {$duration} 分钟。",
            'is_secret' => false,
            'font_color' => '#dc2626',
            'action' => '',
            'sent_at' => now()->toDateTimeString(),
        ];
        $this->chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);

        // 广播禁言事件（前端禁用输入框）
        broadcast(new \App\Events\UserMuted(
            roomId: $roomId,
            username: $targetUsername,
            muteTime: $duration,
            operator: $admin->username,
        ));

        return response()->json(['status' => 'success', 'message' => "已禁言 {$targetUsername} {$duration} 分钟"]);
    }

    /**
     * 冻结用户账号（=Y 理由）
     *
     * 将用户账号状态设为冻结，禁止登录。
     *
     * @param  Request  $request  请求对象，需包含 username, reason
     * @return JsonResponse 操作结果
     */
    public function freeze(Request $request): JsonResponse
    {
        $request->validate([
            'username' => 'required|string',
            'room_id' => 'required|integer',
            'reason' => 'nullable|string|max:200',
        ]);

        $admin = Auth::user();
        $targetUsername = $request->input('username');
        $roomId = $request->input('room_id');
        $reason = $request->input('reason', '违反聊天室规则');

        // 权限检查（等级由 level_freeze 配置）
        if (! $this->canExecute($admin, $targetUsername, 'level_freeze', '14')) {
            return response()->json(['status' => 'error', 'message' => '权限不足'], 403);
        }

        // 冻结用户账号（将等级设为 -1 表示冻结）
        $target = User::where('username', $targetUsername)->first();
        if (! $target) {
            return response()->json(['status' => 'error', 'message' => '用户不存在'], 404);
        }
        $target->user_level = -1;
        $target->save();

        // 从所有房间移除
        $rooms = $this->chatState->getUserRooms($targetUsername);
        foreach ($rooms as $rid) {
            $this->chatState->userLeave($rid, $targetUsername);
        }

        // 广播冻结消息
        $msg = [
            'id' => $this->chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统传音',
            'to_user' => '大家',
            'content' => "🧊 管理员 <b>{$admin->username}</b> 已冻结 <b>{$targetUsername}</b> 的账号。原因：{$reason}",
            'is_secret' => false,
            'font_color' => '#dc2626',
            'action' => '',
            'sent_at' => now()->toDateTimeString(),
        ];
        $this->chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);

        // 广播踢出事件
        broadcast(new \App\Events\UserKicked($roomId, $targetUsername, "账号已被冻结：{$reason}"));

        return response()->json(['status' => 'success', 'message' => "已冻结 {$targetUsername} 的账号"]);
    }

    /**
     * 查看用户私信（=S）
     *
     * 管理员查看指定用户最近的悄悄话记录。
     * AJAX 请求返回 JSON，浏览器请求返回美观的 HTML 页面。
     *
     * @param  string  $username  目标用户名
     * @return JsonResponse|\Illuminate\View\View 私信记录
     */
    public function viewWhispers(Request $request, string $username): JsonResponse|\Illuminate\View\View
    {
        $admin = Auth::user();
        $superLevel = (int) Sysparam::getValue('superlevel', '100');

        if ($admin->user_level < $superLevel) {
            if ($request->expectsJson()) {
                return response()->json(['status' => 'error', 'message' => '仅站长可查看私信'], 403);
            }
            abort(403, '仅站长可查看私信');
        }

        // 查询最近 50 条悄悄话（发送或接收）
        $messages = Message::where('is_secret', true)
            ->where(function ($q) use ($username) {
                $q->where('from_user', $username)
                    ->orWhere('to_user', $username);
            })
            ->orderByDesc('id')
            ->limit(50)
            ->get(['id', 'from_user', 'to_user', 'content', 'sent_at']);

        // AJAX 请求返回 JSON（给名片弹窗用），浏览器请求返回 HTML 页面
        if ($request->expectsJson()) {
            return response()->json([
                'status' => 'success',
                'username' => $username,
                'messages' => $messages,
            ]);
        }

        return view('admin.whispers', [
            'username' => $username,
            'messages' => $messages,
        ]);
    }

    /**
     * 站长公屏讲话
     *
     * 站长发送全聊天室公告，以特殊样式显示。
     *
     * @param  Request  $request  请求对象，需包含 content, room_id
     * @return JsonResponse 操作结果
     */
    public function announce(Request $request): JsonResponse
    {
        $request->validate([
            'content' => 'required|string|max:500',
            'room_id' => 'required|integer',
        ]);

        $admin = Auth::user();
        $superLevel = (int) Sysparam::getValue('superlevel', '100');

        if ($admin->user_level < $superLevel) {
            return response()->json(['status' => 'error', 'message' => '仅站长可发布公屏讲话'], 403);
        }

        $roomId = $request->input('room_id');
        $content = $request->input('content');

        // 广播站长公告
        $msg = [
            'id' => $this->chatState->nextMessageId($roomId),
            'room_id' => $roomId,
            'from_user' => '系统公告',
            'to_user' => '大家',
            'content' => "📢 站长 <b>{$admin->username}</b> 讲话：{$content}",
            'is_secret' => false,
            'font_color' => '#b91c1c',
            'action' => '',
            'sent_at' => now()->toDateTimeString(),
        ];
        $this->chatState->pushMessage($roomId, $msg);
        broadcast(new MessageSent($roomId, $msg));
        SaveMessageJob::dispatch($msg);

        return response()->json(['status' => 'success', 'message' => '公告已发送']);
    }

    /**
     * 管理员全员清屏
     *
     * 清除 Redis 中该房间的聊天记录缓存，并广播清屏事件通知所有用户前端清除消息。
     * 前端只清除普通消息，保留悄悄话。
     *
     * @param  Request  $request  请求对象，需包含 room_id
     * @return JsonResponse 操作结果
     */
    public function clearScreen(Request $request): JsonResponse
    {
        $request->validate([
            'room_id' => 'required|integer',
        ]);

        $admin = Auth::user();
        $roomId = $request->input('room_id');
        $superLevel = (int) Sysparam::getValue('superlevel', '100');

        // 需要站长权限才能全员清屏
        if ($admin->user_level < $superLevel) {
            return response()->json(['status' => 'error', 'message' => '仅站长可执行全员清屏'], 403);
        }

        // 清除 Redis 中该房间的消息缓存
        $this->chatState->clearMessages($roomId);

        // 广播清屏事件
        broadcast(new \App\Events\ScreenCleared($roomId, $admin->username));

        return response()->json(['status' => 'success', 'message' => '已执行全员清屏']);
    }

    /**
     * 管理员触发全屏特效（烟花/下雨/雷电）
     *
     * 向房间内所有用户广播 EffectBroadcast 事件，前端收到后播放对应 Canvas 动画。
     * 仅 superlevel 等级管理员可触发。
     *
     * @param  Request  $request  请求对象，需包含 room_id, type
     * @return JsonResponse 操作结果
     */
    public function effect(Request $request): JsonResponse
    {
        $request->validate([
            'room_id' => 'required|integer',
            'type'    => 'required|in:fireworks,rain,lightning,snow',
        ]);

        $admin      = Auth::user();
        $roomId     = $request->input('room_id');
        $type       = $request->input('type');
        $superLevel = (int) Sysparam::getValue('superlevel', '100');

        // 仅 superlevel 等级可触发特效
        if ($admin->user_level < $superLevel) {
            return response()->json(['status' => 'error', 'message' => '仅站长可触发特效'], 403);
        }

        // 广播特效事件给房间内所有在线用户
        broadcast(new \App\Events\EffectBroadcast($roomId, $type, $admin->username));

        return response()->json(['status' => 'success', 'message' => "已触发特效：{$type}"]);
    }

    /**
     * 权限检查：管理员是否可对目标用户执行指定操作
     *
     * 根据 sysparam 中配置的等级门槛判断权限。
     *
     * @param  User  $admin  管理员用户
     * @param  string  $targetUsername  目标用户名
     * @param  string  $levelKey  sysparam 中的等级键名（如 level_kick、level_warn）
     * @param  string  $defaultLevel  默认等级值
     * @return bool 是否有权限
     */
    private function canExecute(User $admin, string $targetUsername, string $levelKey, string $defaultLevel = '5'): bool
    {
        // 必须达到该操作所需的最低等级
        $requiredLevel = (int) Sysparam::getValue($levelKey, $defaultLevel);
        if ($admin->user_level < $requiredLevel) {
            return false;
        }

        // 不能操作自己
        if ($admin->username === $targetUsername) {
            return false;
        }

        // 目标用户等级必须低于操作者
        $target = User::where('username', $targetUsername)->first();
        if ($target && $target->user_level >= $admin->user_level) {
            return false;
        }

        return true;
    }
}
