<?php

/**
 * 文件功能：用户中心与管理控制器
 * 接管原版 USERinfo.ASP, USERSET.ASP, chpasswd.asp, KILLUSER.ASP, LOCKIP.ASP
 *
 * 权限等级通过 sysparam 表动态配置：
 *   level_kick    - 踢人所需等级
 *   level_mute    - 禁言所需等级
 *   level_ban     - 封号所需等级
 *   level_banip   - 封IP所需等级
 *
 * @author   ChatRoom Laravel
 *
 * @version  1.1.0
 */

namespace App\Http\Controllers;

use App\Events\UserKicked;
use App\Events\UserMuted;
use App\Events\UserStatusUpdated;
use App\Http\Requests\ChangePasswordRequest;
use App\Http\Requests\UpdateChatPreferencesRequest;
use App\Http\Requests\UpdateDailyStatusRequest;
use App\Http\Requests\UpdateProfileRequest;
use App\Models\Room;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\ChatStateService;
use App\Services\ChatUserPresenceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redis;

/**
 * 类功能：处理用户资料、聊天室偏好、当日状态与基础管理动作。
 */
class UserController extends Controller
{
    /**
     * 构造用户控制器依赖。
     */
    public function __construct(
        private readonly ChatStateService $chatState,
        private readonly ChatUserPresenceService $chatUserPresenceService,
    ) {}

    /**
     * 查看其他用户资料片 (对应 USERinfo.ASP)
     */
    public function show(string $username): JsonResponse
    {
        $targetUser = User::where('username', $username)->firstOrFail();
        $operator = Auth::user();

        // 探测原图
        $headfaceOriginal = $targetUser->headfaceUrl;
        if (str_starts_with((string) $targetUser->headface, 'storage/')) {
            $info = pathinfo($targetUser->headface);
            $origPath = $info['dirname'].'/'.$info['filename'].'_original.'.($info['extension'] ?? 'jpg');
            if (\Illuminate\Support\Facades\Storage::disk('public')->exists(substr($origPath, 8))) {
                $headfaceOriginal = '/'.$origPath;
            }
        }

        // 基础公开信息
        $activePosition = $targetUser->activePosition?->load('position.department')->position;
        $data = [
            'username' => $targetUser->username,
            'sex' => match ((int) $targetUser->sex) {
                1 => '男', 2 => '女', default => ''
            },
            'headface' => $targetUser->headface,
            'headface_original' => $headfaceOriginal,
            'usersf' => $targetUser->usersf,
            'user_level' => $targetUser->user_level,
            'qianming' => $targetUser->qianming,
            'sign' => $targetUser->sign ?? '这个人很懒，什么都没留下。',
            'created_at' => $targetUser->created_at->format('Y-m-d'),
            // 在职职务（供名片弹窗任命/撤销判断）
            'position_name' => $activePosition?->name ?? '',
            'position_icon' => $activePosition?->icon ?? '',
            'department_name' => $activePosition?->department?->name ?? '',
            'department_rank' => (int) ($activePosition?->department?->rank ?? 0),
            'position_rank' => (int) ($activePosition?->rank ?? 0),
        ];

        // 只有等级不低于对方，或者自己看自己时，才能看到详细的财富、经验资产
        if ($operator && ($operator->user_level >= $targetUser->user_level || $operator->id === $targetUser->id)) {
            $data['exp_num'] = $targetUser->exp_num ?? 0;
            $data['jjb'] = $targetUser->jjb ?? 0;
            $data['meili'] = $targetUser->meili ?? 0;
        }

        // 银行存款：只有超级管理员（user_level >= superlevel）或本人才能查看具体金额，其余一律显示星号
        if ($operator) {
            $isSelf = $operator->id === $targetUser->id;
            $superLevel = (int) Sysparam::getValue('superlevel', '100');
            $isSuperAdmin = $operator->user_level >= $superLevel;
            $data['bank_jjb'] = ($isSelf || $isSuperAdmin)
                ? ($targetUser->bank_jjb ?? 0)
                : '******';
        }

        // 仅当自己看自己时，附加邀请相关信息，用于展示专属邀请链接
        if ($operator && $operator->id === $targetUser->id) {
            $data['id'] = $targetUser->id;
            $data['invitees_count'] = $targetUser->invitees()->count();
        }

        // 职务履历（所有任职记录，按任命时间倒序；positions() 关系已含 with）
        $data['position_history'] = $targetUser->positions
            ->map(fn ($up) => [
                'position_icon' => $up->position?->icon ?? '',
                'position_name' => $up->position?->name ?? '',
                'department_name' => $up->position?->department?->name ?? '',
                'appointed_at' => $up->appointed_at?->format('Y-m-d'),
                'revoked_at' => $up->revoked_at?->format('Y-m-d'),
                'is_active' => $up->is_active,
                'duration_days' => $up->duration_days,
            ])
            ->values()
            ->all();
        $data['vip']['Name'] = $targetUser->vipName();
        $data['vip']['Icon'] = $targetUser->vipIcon();
        $signIdentity = $targetUser->currentSignInIdentity();
        $latestSignIn = $targetUser->dailySignIns()->first();
        $data['sign_in'] = [
            'streak_days' => (int) ($latestSignIn?->streak_days ?? 0),
            'identity' => $signIdentity ? [
                'key' => $signIdentity->badge_code,
                'label' => $signIdentity->badge_name,
                'icon' => $signIdentity->badge_icon ?? '✅',
                'color' => $signIdentity->badge_color ?? '#0f766e',
                'expires_at' => $signIdentity->expires_at?->toIso8601String(),
            ] : null,
        ];

        // 拥有封禁IP（level_banip）或踢人以上权限的管理，可以查看IP和归属地
        $levelBanIp = (int) Sysparam::getValue('level_banip', '15');
        if ($operator && $operator->user_level >= $levelBanIp) {
            $data['first_ip'] = $targetUser->first_ip;
            // last_ip 目前定义为『上次登录IP』（取数据库 previous_ip）
            $data['last_ip'] = $targetUser->previous_ip;
            // login_ip 目前定义为『本次登录IP』（取数据库 last_ip）
            $data['login_ip'] = $targetUser->last_ip;

            // 解析归属地：使用 ip2region 离线库，直接返回原生中文（省|市|ISP）
            $ipToLookup = $targetUser->last_ip;
            if ($ipToLookup) {
                try {
                    // 不传路径，使用 zoujingli/ip2region 包自带的内置数据库
                    $ip2r = new \Ip2Region;
                    $info = $ip2r->getIpInfo($ipToLookup);

                    if ($info) {
                        $country = $info['country'] ?? '';
                        $province = $info['province'] ?? '';
                        $city = $info['city'] ?? '';

                        // 过滤掉占位符 "0"
                        $province = ($province === '0') ? '' : $province;
                        $city = ($city === '0') ? '' : $city;

                        if ($country === '中国') {
                            $data['location'] = trim($province.($province !== $city ? ' '.$city : ''));
                        } else {
                            $data['location'] = $country ?: '未知区域';
                        }

                        if (empty($data['location'])) {
                            $data['location'] = '未知区域';
                        }
                    } else {
                        $data['location'] = '未知区域';
                    }
                } catch (\Exception $e) {
                    $data['location'] = '解析失败';
                }
            } else {
                $data['location'] = '暂无记录';
            }
        }

        return response()->json([
            'status' => 'success',
            'data' => $data,
        ]);
    }

    /**
     * 修改个人资料 (对应 USERSET.ASP)
     */
    public function updateProfile(UpdateProfileRequest $request): JsonResponse
    {
        $user = Auth::user();
        $data = $request->validated();

        // 当用户试图更新邮箱，并且新邮箱不等于当前旧邮箱时启动验证码拦截
        if (isset($data['email']) && $data['email'] !== $user->email) {
            // 首先判断系统开关是否开启，没开启直接禁止修改邮箱
            if (\App\Models\SysParam::where('alias', 'smtp_enabled')->value('body') !== '1') {
                return response()->json(['status' => 'error', 'message' => '系统未开启邮件服务，当前禁止绑定/修改邮箱。'], 403);
            }

            $emailCode = $request->input('email_code');
            if (empty($emailCode)) {
                return response()->json(['status' => 'error', 'message' => '新邮箱需要验证码，请先获取并填写验证码。'], 422);
            }

            // 获取缓存的验证码
            $codeKey = 'email_verify_code_'.$user->id.'_'.$data['email'];
            $cachedCode = \Illuminate\Support\Facades\Cache::get($codeKey);

            if (! $cachedCode || $cachedCode != $emailCode) {
                return response()->json(['status' => 'error', 'message' => '验证码不正确或已过期（有效期5分钟），请重新获取。'], 422);
            }

            // 验证成功后，立即核销该验证码防止二次利用
            \Illuminate\Support\Facades\Cache::forget($codeKey);
        }

        if (isset($data['headface']) && $data['headface'] !== $user->headface) {
            $user->deleteCustomAvatar();
        }

        $user->update($data);

        return response()->json(['status' => 'success', 'message' => '资料更新成功。']);
    }

    /**
     * 保存聊天室屏蔽与禁音偏好。
     */
    public function updateChatPreferences(UpdateChatPreferencesRequest $request): JsonResponse
    {
        $user = Auth::user();
        $data = $request->validated();

        $preferences = [
            // 去重并重建索引，保持存储结构稳定，便于后续继续扩展其它屏蔽项。
            'blocked_system_senders' => array_values(array_unique($data['blocked_system_senders'] ?? [])),
            'sound_muted' => (bool) $data['sound_muted'],
        ];

        $user->update([
            'chat_preferences' => $preferences,
        ]);

        return response()->json([
            'status' => 'success',
            'message' => '聊天室偏好已保存。',
            'data' => $preferences,
        ]);
    }

    /**
     * 保存聊天室当日状态，并同步当前在线名单显示。
     */
    public function updateDailyStatus(UpdateDailyStatusRequest $request): JsonResponse
    {
        $user = Auth::user();
        $data = $request->validated();
        $roomId = (int) $data['room_id'];

        // 仅允许当前确实在线的用户从聊天室内修改状态，避免离线脏请求写入。
        if (! $this->chatState->isUserInRoom($roomId, $user->username)) {
            return response()->json([
                'status' => 'error',
                'message' => '请先进入聊天室后再设置状态。',
            ], 422);
        }

        if ($data['action'] === 'clear') {
            $user->update([
                'daily_status_key' => null,
                'daily_status_expires_at' => null,
            ]);
        } else {
            // 状态有效期固定维持到当天结束，次日自动失效。
            $user->update([
                'daily_status_key' => $data['status_key'],
                'daily_status_expires_at' => now()->endOfDay(),
            ]);
        }

        $user->refresh();
        $presencePayload = $this->chatUserPresenceService->build($user);
        $roomIds = $this->chatState->getUserRooms($user->username);

        foreach ($roomIds as $activeRoomId) {
            // 所有当前在线房间都刷新 Redis 载荷，确保头像、会员与状态显示口径一致。
            $this->chatState->userJoin((int) $activeRoomId, $user->username, $presencePayload);
            broadcast(new UserStatusUpdated((int) $activeRoomId, $user->username, $presencePayload));
        }

        return response()->json([
            'status' => 'success',
            'message' => $data['action'] === 'clear' ? '状态已清除。' : '状态已更新。',
            'data' => [
                'status' => $this->chatUserPresenceService->currentDailyStatus($user),
            ],
        ]);
    }

    /**
     * 修改密码 (对应 chpasswd.asp)
     */
    public function changePassword(ChangePasswordRequest $request): JsonResponse
    {
        $user = Auth::user();
        $oldPasswordInput = $request->input('old_password');

        // 双模式密码校验逻辑（沿用 AuthController 中策略）
        $isOldPasswordCorrect = false;

        // 优先验证 Bcrypt
        if (Hash::check($oldPasswordInput, $user->password)) {
            $isOldPasswordCorrect = true;
        }
        // 降级验证旧版 MD5
        elseif (md5($oldPasswordInput) === $user->password) {
            $isOldPasswordCorrect = true;
        }

        if (! $isOldPasswordCorrect) {
            return response()->json(['status' => 'error', 'message' => '当前密码输入不正确。'], 422);
        }

        // 验证通过，覆盖为新的 Bcrypt 加密串
        $user->password = Hash::make($request->input('new_password'));
        $user->save();

        return response()->json(['status' => 'success', 'message' => '密码已成功修改。下次请使用新密码登录。']);
    }

    /**
     * 生成微信绑定代码
     */
    public function generateWechatCode(\Illuminate\Http\Request $request): JsonResponse
    {
        $user = \Illuminate\Support\Facades\Auth::user();
        if (! $user) {
            return response()->json(['status' => 'error', 'message' => '未登录']);
        }

        $code = 'BD-'.mt_rand(100000, 999999);
        \Illuminate\Support\Facades\Cache::put('wechat_bind_code:'.$code, $user->username, 300); // 5分钟有效

        return response()->json([
            'status' => 'success',
            'code' => $code,
            'message' => '生成成功',
        ]);
    }

    /**
     * 取消绑定微信
     */
    public function unbindWechat(\Illuminate\Http\Request $request): JsonResponse
    {
        $user = \Illuminate\Support\Facades\Auth::user();
        if (! $user) {
            return response()->json(['status' => 'error', 'message' => '未登录']);
        }

        $user->wxid = null;
        $user->save();

        return response()->json([
            'status' => 'success',
            'message' => '解绑成功',
        ]);
    }

    /**
     * 通用权限校验：检查操作者是否有权操作目标用户
     *
     * @param  object  $operator  操作者
     * @param  string  $targetUsername  目标用户名
     * @param  int  $roomId  房间ID
     * @param  string  $levelKey  sysparam中的等级键名（如 level_kick）
     * @param  string  $actionName  操作名称（用于错误提示）
     * @return array{room: Room, target: User}|JsonResponse
     */
    private function checkPermission(object $operator, string $targetUsername, int $roomId, string $levelKey, string $actionName): array|JsonResponse
    {
        $room = Room::findOrFail($roomId);
        $requiredLevel = (int) Sysparam::getValue($levelKey, '15');

        // 鉴权：操作者要是房间房主或达到所需等级
        if ($room->master !== $operator->username && $operator->user_level < $requiredLevel) {
            return response()->json(['status' => 'error', 'message' => "权限不足（需要{$requiredLevel}级），无法执行{$actionName}操作。"], 403);
        }

        $targetUser = User::where('username', $targetUsername)->first();
        if (! $targetUser) {
            return response()->json(['status' => 'error', 'message' => '目标用户不存在。'], 404);
        }

        // 防误伤：不能操作等级 >= 自己的人
        if ($targetUser->user_level >= $operator->user_level) {
            return response()->json(['status' => 'error', 'message' => "权限不足，无法对同级或高级用户执行{$actionName}。"], 403);
        }

        return ['room' => $room, 'target' => $targetUser];
    }

    /**
     * 踢出房间 (对应 KILLUSER.ASP)
     * 所需等级由 sysparam level_kick 配置
     */
    public function kick(Request $request, string $username): JsonResponse
    {
        $operator = Auth::user();
        $roomId = $request->input('room_id');

        if (! $roomId) {
            return response()->json(['status' => 'error', 'message' => '缺少房间参数。'], 422);
        }

        $result = $this->checkPermission($operator, $username, $roomId, 'level_kick', '踢出');
        if ($result instanceof JsonResponse) {
            return $result;
        }

        // 广播踢出事件
        broadcast(new UserKicked($roomId, $result['target']->username, "管理员 [{$operator->username}] 将 [{$result['target']->username}] 踢出了聊天室。"));

        return response()->json(['status' => 'success', 'message' => "已成功将 {$result['target']->username} 踢出房间。"]);
    }

    /**
     * 禁言 (对应原版限制功能)
     * 所需等级由 sysparam level_mute 配置
     * 禁言信息存入 Redis，TTL 到期自动解除
     */
    public function mute(Request $request, string $username): JsonResponse
    {
        $operator = Auth::user();
        $roomId = $request->input('room_id');
        $duration = (int) $request->input('duration', 5);

        if (! $roomId) {
            return response()->json(['status' => 'error', 'message' => '缺少房间参数。'], 422);
        }

        $result = $this->checkPermission($operator, $username, $roomId, 'level_mute', '禁言');
        if ($result instanceof JsonResponse) {
            return $result;
        }

        // 写入 Redis 禁言标记，TTL = 禁言分钟数 * 60
        Redis::setex("mute:{$roomId}:{$username}", $duration * 60, json_encode([
            'operator' => $operator->username,
            'reason' => '管理员禁言',
            'until' => now()->addMinutes($duration)->toDateTimeString(),
        ]));

        // 广播禁言事件
        broadcast(new UserMuted($roomId, $username, $duration));

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

    /**
     * 封号（禁止登录）
     * 所需等级由 sysparam level_ban 配置
     * 将用户等级设为 -1 表示封禁
     */
    public function ban(Request $request, string $username): JsonResponse
    {
        $operator = Auth::user();
        $roomId = $request->input('room_id');

        if (! $roomId) {
            return response()->json(['status' => 'error', 'message' => '缺少房间参数。'], 422);
        }

        $result = $this->checkPermission($operator, $username, $roomId, 'level_ban', '封号');
        if ($result instanceof JsonResponse) {
            return $result;
        }

        // 封号：设置等级为 -1
        $result['target']->user_level = -1;
        $result['target']->save();

        // 踢出聊天室
        broadcast(new UserKicked($roomId, $username, "管理员 [{$operator->username}] 已封禁用户 [{$username}] 的账号。"));

        return response()->json(['status' => 'success', 'message' => "用户 {$username} 已被封号。"]);
    }

    /**
     * 封IP（记录IP到黑名单并踢出）
     * 所需等级由 sysparam level_banip 配置
     */
    public function banIp(Request $request, string $username): JsonResponse
    {
        $operator = Auth::user();
        $roomId = $request->input('room_id');

        if (! $roomId) {
            return response()->json(['status' => 'error', 'message' => '缺少房间参数。'], 422);
        }

        $result = $this->checkPermission($operator, $username, $roomId, 'level_banip', '封IP');
        if ($result instanceof JsonResponse) {
            return $result;
        }

        $targetIp = $result['target']->last_ip;

        if ($targetIp) {
            // 将IP加入 Redis 黑名单（永久）
            Redis::sadd('banned_ips', $targetIp);
        }

        // 同时封号
        $result['target']->user_level = -1;
        $result['target']->save();

        // 踢出聊天室
        broadcast(new UserKicked($roomId, $username, "管理员 [{$operator->username}] 已封禁用户 [{$username}] 的IP地址。"));

        $ipInfo = $targetIp ? "（IP: {$targetIp}）" : '（未记录IP）';

        return response()->json(['status' => 'success', 'message' => "用户 {$username} 已被封号并封IP{$ipInfo}。"]);
    }
}
