<?php

/**
 * 文件功能：用户离开聊天室后的异步清理与播报任务
 * 负责清理在线状态、关闭勤务日志，并根据会员/管理员身份发送不同的离场提示。
 */

namespace App\Jobs;

use App\Models\PositionDutyLog;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\ChatStateService;
use App\Services\RoomBroadcastService;
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\Redis;

class ProcessUserLeave implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * 构造离场处理任务实例。
     */
    public function __construct(
        public int $roomId,
        public User $user,
        public float $leaveTime,
        public string $outInfo = '正常退出了房间',
    ) {}

    /**
     * 执行离场任务：清理在线状态并广播离场消息。
     */
    public function handle(ChatStateService $chatState, RoomBroadcastService $broadcast): void
    {
        // 获取该用户最后一次进入房间的时间
        $lastJoinTime = (float) Redis::get("room:{$this->roomId}:join_time:{$this->user->username}");

        // 如果最后一次加入的时间 > 当前离线任务产生的时间，说明用户又刷新重新进来了
        if ($lastJoinTime >= $this->leaveTime) {
            return;
        }

        // 1. 从 Redis 删除该用户
        $chatState->userLeave($this->roomId, $this->user->username);

        // 记录退出时间和退出信息
        $this->user->update([
            'out_time' => now(),
            'out_info' => $this->outInfo,
        ]);

        // 关闭该用户尚未结束的在职登录记录（结算在线时长）
        $this->closeDutyLog($this->user->id);

        // 2. 发送离场播报
        $superLevel = (int) Sysparam::getValue('superlevel', '100');
       
        [$leaveText, $color] = $broadcast->buildLeaveBroadcast($this->user);
        $vipPresencePayload = $broadcast->buildVipPresencePayload($this->user, 'leave');
        $leaveMsg = [
            'id' => $chatState->nextMessageId($this->roomId),
            'room_id' => $this->roomId,
            'from_user' => '进出播报',
            'to_user' => '大家',
            'content' => "<span style=\"color: {$color}; font-weight: bold;\">{$leaveText}</span>",
            'is_secret' => false,
            'font_color' => $color,
            'action' => empty($vipPresencePayload) ? 'system_welcome' : 'vip_presence',
            'welcome_user' => $this->user->username,
            'sent_at' => now()->toDateTimeString(),
        ];

        // 会员离场时，把横幅与特效信息挂到消息体，前端才能展示专属离场效果。
        if (! empty($vipPresencePayload)) {
            $leaveMsg = array_merge($leaveMsg, $vipPresencePayload);
        }

        // 将播报存入 Redis 历史及广播
        $chatState->pushMessage($this->roomId, $leaveMsg);
        broadcast(new \App\Events\UserLeft($this->roomId, $this->user->username))->toOthers();
        broadcast(new \App\Events\MessageSent($this->roomId, $leaveMsg))->toOthers();

        // 离场特效单独发送给房间内仍在线的其他人，避免和消息播报逻辑耦死。
        if (! empty($leaveMsg['presence_effect'])) {
            broadcast(new \App\Events\EffectBroadcast($this->roomId, $leaveMsg['presence_effect'], $this->user->username))->toOthers();
        }
    }

    /**
     * 关闭该用户尚未结束的在职登录记录（结算在线时长）
     */
    private function closeDutyLog(int $userId): void
    {
        // 将今日开放日志关闭并结算实际时长
        PositionDutyLog::query()
            ->where('user_id', $userId)
            ->whereNull('logout_at')
            ->whereDate('login_at', today())
            ->update([
                'logout_at' => now(),
                'duration_seconds' => DB::raw('GREATEST(0, TIMESTAMPDIFF(SECOND, login_at, NOW()))'),
            ]);

        // 关闭历史遗留的跨天未关闭日志（login_at 非今日）
        PositionDutyLog::query()
            ->where('user_id', $userId)
            ->whereNull('logout_at')
            ->whereDate('login_at', '!=', today())
            ->update([
                'logout_at' => DB::raw('DATE_ADD(DATE(login_at), INTERVAL "23:59:59" HOUR_SECOND)'),
            ]);
    }
}
