package com.zcloud.zcGbsServicer.service;

import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.zcloud.zcGbsServicer.api.VideoPlatformServiceI;
import com.zcloud.zcGbsServicer.dto.VideoPlatformDepartmentQry;
import com.zcloud.zcGbsServicer.dto.VideoPlatformFixedRoomLeaveCmd;
import com.zcloud.zcGbsServicer.dto.VideoPlatformFixedRoomLoginCmd;
import com.zcloud.zcGbsServicer.dto.VideoPlatformFixedRoomQry;
import com.zcloud.zcGbsServicer.dto.VideoPlatformMeetingAccessCheckQry;
import com.zcloud.zcGbsServicer.dto.VideoPlatformMeetingMemberQry;
import com.zcloud.zcGbsServicer.dto.VideoPlatformUserQry;
import com.zcloud.zcGbsServicer.dto.VideoRoomAuthRoomCmd;
import com.zcloud.zcGbsServicer.dto.VideoRoomAuthUserCmd;
import com.zcloud.zcGbsServicer.dto.VideoRoomCreateInstantCmd;
import com.zcloud.zcGbsServicer.dto.VideoRoomOnlineRoomQry;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformConfigCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformCurrentMeetingCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformLoginCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformMeetingAccessCheckCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformMeetingLogCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformMeetingMemberStatusCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformMeetingParticipantCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformOnlineRoomCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformOnlineRoomPageCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformResultCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoPlatformTokenCO;
import com.zcloud.zcGbsServicer.dto.clientobject.VideoRoomInstantCO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireAlarmMeetingRoomDO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireAlarmMeetingUserDO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireBrigadeDO;
import com.zcloud.zcGbsServicer.persistence.dataobject.FireBrigadeUserDO;
import com.zcloud.zcGbsServicer.persistence.repository.FireAlarmMeetingRoomRepository;
import com.zcloud.zcGbsServicer.persistence.repository.FireAlarmMeetingUserRepository;
import com.zcloud.zcGbsServicer.persistence.repository.FireBrigadeRepository;
import com.zcloud.zcGbsServicer.persistence.repository.FireBrigadeUserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@SuppressWarnings("unchecked")
public class VideoPlatformServiceImpl extends FireAlarmServiceSupport implements VideoPlatformServiceI {

    private static final int AUTH_STATUS_SUCCESS = 1;
    private static final int MAX_MEETING_TOTAL_COUNT = 4;
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final HstVideoPlatformProperties properties;
    private final HstVideoPlatformClient hstVideoPlatformClient;
    private final FireAlarmMeetingRoomRepository fireAlarmMeetingRoomRepository;
    private final FireAlarmMeetingUserRepository fireAlarmMeetingUserRepository;
    private final FireBrigadeRepository fireBrigadeRepository;
    private final FireBrigadeUserRepository fireBrigadeUserRepository;
    private final FireAlarmUserResolver fireAlarmUserResolver;

    @Override
    public SingleResponse<VideoPlatformConfigCO> getConfig() {
        VideoPlatformConfigCO co = new VideoPlatformConfigCO();
        co.setApiBaseUrl(properties.getApiBaseUrl());
        co.setWebBaseUrl(properties.getWebBaseUrl());
        co.setClientPort(properties.getClientPort());
        co.setDefaultRoomCode(properties.getDefaultRoomCode());
        co.setDefaultRoomNo(properties.getDefaultRoomNo());
        co.setDefaultUserPassword(properties.getDefaultUserPassword());
        co.setTestAccounts(properties.getTestAccounts());
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformTokenCO> generateToken() {
        VideoPlatformTokenCO co = new VideoPlatformTokenCO();
        co.setToken(hstVideoPlatformClient.generateToken());
        co.setExpireTime(hstVideoPlatformClient.formatExpireTime());
        co.setApiBaseUrl(properties.getApiBaseUrl());
        co.setWebBaseUrl(properties.getWebBaseUrl());
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoRoomInstantCO> createInstant(VideoRoomCreateInstantCmd cmd) {
        String meetingName = StrUtil.blankToDefault(cmd.getMeetingName(), "消防报警即时会议");
        String userName = StrUtil.blankToDefault(cmd.getUserName(), currentUserName());
        if (StrUtil.isBlank(userName)) {
            userName = "消防报警";
        }

        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("meetingName", meetingName);
        requestBody.put("userName", userName);
        putIfNotNull(requestBody, "meetingTemplate", cmd.getMeetingTemplate());
        putIfNotNull(requestBody, "meetingType", cmd.getMeetingType());
        putIfNotNull(requestBody, "verifyMode", cmd.getVerifyMode());
        putIfNotNull(requestBody, "departId", cmd.getDepartId());
        putIfNotNull(requestBody, "maxUserCount", cmd.getMaxUserCount());
        putIfNotBlank(requestBody, "password", cmd.getPassword());
        putIfNotBlank(requestBody, "chairPassword", cmd.getChairPassword());
        putIfNotBlank(requestBody, "enableInvite", cmd.getEnableInvite());
        putIfNotBlank(requestBody, "enableChairPwd", cmd.getEnableChairPwd());
        putIfNotBlank(requestBody, "autoRecord", cmd.getAutoRecord());
        putIfNotBlank(requestBody, "roomMode", cmd.getRoomMode());
        putIfNotBlank(requestBody, "source", cmd.getSource());
        putIfNotBlank(requestBody, "menteeAdvance", cmd.getMenteeAdvance());

        Map<String, Object> response = hstVideoPlatformClient.createInstant(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "创建即时会议室");
        Map<String, Object> data = castMap(response.get("data"));

        VideoRoomInstantCO co = new VideoRoomInstantCO();
        co.setMeetingName(selectText(asText(data.get("meetingName")), meetingName));
        co.setRoomId(asText(data.get("roomId")));
        co.setPassword(selectText(asText(data.get("password")), cmd.getPassword()));
        co.setUserName(userName);
        co.setCesAddr(properties.getWebBaseUrl());
        co.setWebUrl(properties.getWebBaseUrl());
        co.setToken(hstVideoPlatformClient.generateToken());
        co.setUserPass(properties.getDefaultUserPassword());
        co.setGrantType("password");
        co.setExpireTime(hstVideoPlatformClient.formatExpireTime());
        co.setResultMsg(selectText(asText(response.get("msg")), "即时会议室创建成功"));
        co.setInviteCode(asText(data.get("inviteCode")));
        co.setMaxUserCount(asInteger(data.get("maxUserCount")));
        co.setVerifyMode(asInteger(data.get("verifyMode")));
        co.setMeetingType(asInteger(data.get("meetingType")));
        co.setMeetingTemplate(asInteger(data.get("meetingTemplate")));
        co.setCreateTime(asText(data.get("createTime")));
        co.setCreatorId(asText(data.get("creatorId")));
        co.setLoginAddrList(safeGetLoginAddr());
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformResultCO> authUser(VideoRoomAuthUserCmd cmd) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("roomId", toNumericOrText(String.valueOf(cmd.getRoomId())));
        requestBody.put("roomUserStr", cmd.getRoomUserStr().trim());
        requestBody.put("authType", cmd.getAuthType() == null ? 1 : cmd.getAuthType());
        Map<String, Object> response = hstVideoPlatformClient.authUser(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "会议室授权用户");
        return SingleResponse.of(buildResultCO(response));
    }

    @Override
    public SingleResponse<VideoPlatformResultCO> authRoom(VideoRoomAuthRoomCmd cmd) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("roomUserStr", cmd.getRoomUserStr().trim());
        requestBody.put("userName", cmd.getUserName().trim());
        Map<String, Object> response = hstVideoPlatformClient.authRoom(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "用户授权会议室");
        return SingleResponse.of(buildResultCO(response));
    }

    @Override
    public List<String> getLoginAddr() {
        Map<String, Object> response = hstVideoPlatformClient.loginAddr();
        hstVideoPlatformClient.ensureSuccess(response, "获取登录地址");
        Object data = response.get("data");
        if (!(data instanceof List)) {
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<>();
        for (Object item : (List<?>) data) {
            if (item != null) {
                result.add(String.valueOf(item));
            }
        }
        return result;
    }

    @Override
    public SingleResponse<VideoPlatformOnlineRoomPageCO> onlineRoom(VideoRoomOnlineRoomQry qry) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        putIfNotNull(requestBody, "departId", qry.getDepartId());
        requestBody.put("pageNumber", qry.getPageNumber() == null || qry.getPageNumber() <= 0 ? 1 : qry.getPageNumber());
        requestBody.put("pageSize", qry.getPageSize() == null || qry.getPageSize() <= 0 ? 10 : qry.getPageSize());

        Map<String, Object> response = hstVideoPlatformClient.onlineRoom(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "查询在线会议");
        Map<String, Object> data = castMap(response.get("data"));

        VideoPlatformOnlineRoomPageCO co = new VideoPlatformOnlineRoomPageCO();
        co.setCurrent(asInteger(data.get("current")));
        co.setPages(asInteger(data.get("pages")));
        co.setSize(asInteger(data.get("size")));
        co.setTotal(asInteger(data.get("total")));
        co.setRecords(buildOnlineRoomRecords(data.get("records")));
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformResultCO> departmentList(VideoPlatformDepartmentQry qry) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        if (StrUtil.isNotBlank(qry.getDepartId())) {
            requestBody.put("departId", toNumericOrText(qry.getDepartId()));
        }
        putIfNotBlank(requestBody, "departName", qry.getDepartName());
        putIfNotBlank(requestBody, "code", qry.getCode());
        requestBody.put("pageNumber", qry.getPageNumber() == null || qry.getPageNumber() <= 0 ? 1 : qry.getPageNumber());
        requestBody.put("pageSize", qry.getPageSize() == null || qry.getPageSize() <= 0 ? 100 : qry.getPageSize());

        Map<String, Object> response = hstVideoPlatformClient.departmentList(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "查询视频平台组织架构");
        return SingleResponse.of(buildResultCO(response));
    }

    @Override
    public SingleResponse<VideoPlatformResultCO> userList(VideoPlatformUserQry qry) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        putIfNotBlank(requestBody, "searchKey", qry.getSearchKey());
        putIfNotBlank(requestBody, "searchType", qry.getSearchType());
        if (StrUtil.isNotBlank(qry.getDepartId())) {
            requestBody.put("departId", toNumericOrText(qry.getDepartId()));
        }
        requestBody.put("pageNumber", qry.getPageNumber() == null || qry.getPageNumber() <= 0 ? 1 : qry.getPageNumber());
        requestBody.put("pageSize", qry.getPageSize() == null || qry.getPageSize() <= 0 ? 100 : qry.getPageSize());

        Map<String, Object> response = hstVideoPlatformClient.userList(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "查询视频平台用户");
        return SingleResponse.of(buildResultCO(response));
    }

    @Override
    public SingleResponse<VideoPlatformCurrentMeetingCO> currentFixedMeeting(VideoPlatformFixedRoomQry qry) {
        FixedRoomContext context = resolveFixedRoom(qry.getRoomCode());
        VideoPlatformOnlineRoomCO onlineRoom = findOnlineFixedRoom(context);
        FireAlarmMeetingUserDO currentMeetingUser = resolveCurrentMeetingUser(qry.getLocalUserId(), false);

        VideoPlatformCurrentMeetingCO co = new VideoPlatformCurrentMeetingCO();
        co.setRoomCode(context.roomCode);
        co.setRoomId(context.roomId);
        co.setRoomNo(context.roomNo);
        co.setRoomName(onlineRoom == null ? context.roomName : selectText(onlineRoom.getRoomName(), context.roomName));
        co.setOnline(onlineRoom != null);
        co.setCurUserCount(onlineRoom == null ? 0 : onlineRoom.getCurUserCount());
        co.setStatus(onlineRoom == null ? null : onlineRoom.getStatus());
        co.setStartTime(onlineRoom == null ? null : onlineRoom.getStartTime());
        co.setEndTime(onlineRoom == null ? null : onlineRoom.getEndTime());
        co.setThirdUserName(currentMeetingUser == null ? null : currentMeetingUser.getThirdUserName());
        co.setUserPass(properties.getDefaultUserPassword());
        co.setGrantType("password");
        co.setCesAddr(properties.getWebBaseUrl());
        co.setWebUrl(properties.getWebBaseUrl());
        co.setResultMsg(onlineRoom == null ? "固定会议当前未在线" : "固定会议正在进行");
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformMeetingAccessCheckCO> checkFixedMeetingAccess(VideoPlatformMeetingAccessCheckQry qry) {
        FixedRoomContext context = resolveFixedRoom(qry.getRoomCode());
        Long localUserId = qry.getLocalUserId() == null ? currentUserId() : qry.getLocalUserId();
        if (localUserId == null) {
            throw new BizException("当前登录用户不存在");
        }

        FireAlarmMeetingUserDO meetingUser = resolveCurrentMeetingUser(localUserId, true);
        FireBrigadeUserDO relation = resolveUserBrigadeRelation(localUserId, qry.getFireBrigadeBizId());
        FireBrigadeDO brigade = relation == null ? null : fireBrigadeRepository.getByFireBrigadeId(relation.getFireBrigadeId());

        VideoPlatformMeetingAccessCheckCO co = new VideoPlatformMeetingAccessCheckCO();
        co.setAllowed(false);
        co.setRoomCode(context.roomCode);
        co.setRoomId(context.roomId);
        co.setRoomName(context.roomName);
        co.setLocalUserId(localUserId);
        co.setLocalUserName(meetingUser == null ? null : meetingUser.getLocalUserName());
        co.setThirdUserName(meetingUser == null ? null : meetingUser.getThirdUserName());
        co.setUserPass(properties.getDefaultUserPassword());

        if (meetingUser == null || StrUtil.isBlank(meetingUser.getThirdUserName())) {
            co.setReason("当前用户未配置视频会议账号");
            co.setParticipants(buildParticipants(context, localUserId));
            fillMeetingCapacity(co, co.getParticipants());
            return SingleResponse.of(co);
        }

        if (relation != null) {
            co.setFireBrigadeId(relation.getFireBrigadeId());
            co.setFireBrigadeName(brigade == null ? meetingUser.getFireBrigadeName() : brigade.getBrigadeName());
        }

        List<VideoPlatformMeetingParticipantCO> participants = buildParticipants(context, localUserId);
        co.setParticipants(participants);
        fillMeetingCapacity(co, participants);

        if (isUserInMeeting(participants, localUserId)) {
            co.setAllowed(true);
            co.setOccupied(false);
            co.setReason("当前用户已在会议中");
            return SingleResponse.of(co);
        }

        if (participants.size() >= MAX_MEETING_TOTAL_COUNT) {
            co.setAllowed(false);
            co.setOccupied(false);
            co.setReason("当前参会人数已满，报警需要排队");
            return SingleResponse.of(co);
        }

        if (relation != null) {
            String fireBrigadeId = relation.getFireBrigadeId();
            for (VideoPlatformMeetingParticipantCO participant : participants) {
                if (!Objects.equals(fireBrigadeId, participant.getFireBrigadeId())) {
                    continue;
                }
                if (Objects.equals(localUserId, participant.getLocalUserId())) {
                    continue;
                }
                co.setAllowed(false);
                co.setOccupied(true);
                co.setOccupiedUserId(participant.getLocalUserId());
                co.setOccupiedUserName(participant.getLocalUserName());
                co.setOccupiedThirdUserName(participant.getThirdUserName());
                co.setReason("当前消防队伍已有成员在会议中，同队每次仅允许1人入会");
                return SingleResponse.of(co);
            }
        }

        co.setAllowed(true);
        co.setOccupied(false);
        co.setReason("允许入会");
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformMeetingMemberStatusCO> fixedMeetingMembers(VideoPlatformMeetingMemberQry qry) {
        FixedRoomContext context = resolveFixedRoom(qry.getRoomCode());
        List<VideoPlatformMeetingParticipantCO> participants = buildParticipants(context, currentUserId());

        VideoPlatformMeetingMemberStatusCO co = new VideoPlatformMeetingMemberStatusCO();
        co.setRoomCode(context.roomCode);
        co.setRoomId(context.roomId);
        co.setRoomName(context.roomName);
        co.setParticipantCount(participants.size());
        co.setTotalCount(participants.size());
        co.setMaxTotalCount(MAX_MEETING_TOTAL_COUNT);
        co.setParticipants(participants);
        if (Boolean.FALSE.equals(qry.getIncludeLogs())) {
            co.setLogQuerySuccess(true);
            co.setLogQueryMsg("未查询进退会日志");
            co.setLogs(Collections.emptyList());
        }
        else {
            try {
                co.setLogs(buildRoomLogs(context, qry));
                co.setLogQuerySuccess(true);
                co.setLogQueryMsg("查询成功");
            }
            catch (BizException ex) {
                co.setLogs(Collections.emptyList());
                co.setLogQuerySuccess(false);
                co.setLogQueryMsg(ex.getMessage());
            }
        }
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformLoginCO> fixedRoomLogin(VideoPlatformFixedRoomLoginCmd cmd) {
        VideoPlatformMeetingAccessCheckQry checkQry = new VideoPlatformMeetingAccessCheckQry();
        checkQry.setRoomCode(cmd.getRoomCode());
        checkQry.setLocalUserId(cmd.getLocalUserId());
        VideoPlatformMeetingAccessCheckCO access = checkFixedMeetingAccess(checkQry).getData();
        if (access == null || !Boolean.TRUE.equals(access.getAllowed())) {
            throw new BizException(access == null ? "当前用户不允许入会" : access.getReason());
        }

        FixedRoomContext context = resolveFixedRoom(cmd.getRoomCode());
        String thirdUserName = StrUtil.blankToDefault(cmd.getThirdUserName(), access.getThirdUserName());
        if (StrUtil.isBlank(thirdUserName)) {
            throw new BizException("当前用户未配置第三方会议账号");
        }

        VideoPlatformLoginCO co = new VideoPlatformLoginCO();
        co.setRoomCode(context.roomCode);
        co.setRoomId(context.roomId);
        co.setRoomNo(context.roomNo);
        co.setMeetingName(context.roomName);
        co.setPassword(context.roomPassword);
        co.setThirdUserName(thirdUserName.trim());
        co.setToken(null);
        co.setUserPass(properties.getDefaultUserPassword());
        co.setGrantType("password");
        co.setCesAddr(properties.getWebBaseUrl());
        co.setWebUrl(properties.getWebBaseUrl());
        co.setLoginAddrList(Collections.emptyList());
        co.setLoginUrl(properties.getWebBaseUrl());
        co.setAuthStatus(AUTH_STATUS_SUCCESS);
        co.setAuthorizedUserCount(null);
        co.setResultMsg("固定会议室入会参数获取成功");
        co.setClientId(properties.getClientId());
        co.setClientSecret(properties.getClientSecret());
        return SingleResponse.of(co);
    }

    @Override
    public SingleResponse<VideoPlatformResultCO> fixedRoomLeave(VideoPlatformFixedRoomLeaveCmd cmd) {
        VideoPlatformResultCO co = new VideoPlatformResultCO();
        co.setCode("200");
        co.setMsg("离会成功");
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("localUserId", cmd.getLocalUserId() == null ? currentUserId() : cmd.getLocalUserId());
        data.put("alarmId", cmd.getAlarmId());
        data.put("traceMode", "frontend-sdk");
        co.setData(data);
        return SingleResponse.of(co);
    }

    private FixedRoomContext resolveFixedRoom(String roomCode) {
        String actualRoomCode = StrUtil.blankToDefault(roomCode, properties.getDefaultRoomCode());
        FireAlarmMeetingRoomDO room = fireAlarmMeetingRoomRepository.getByRoomCode(actualRoomCode);
        FixedRoomContext context = new FixedRoomContext();
        context.room = room;
        context.roomCode = actualRoomCode;
        context.roomId = resolveRoomId(room);
        context.roomNo = resolveRoomNo(room);
        context.roomName = resolveRoomName(room);
        context.roomPassword = room == null ? null : room.getRoomPasswordCipher();
        return context;
    }

    private FireAlarmMeetingUserDO resolveCurrentMeetingUser(Long localUserId, boolean required) {
        Long actualLocalUserId = localUserId == null ? currentUserId() : localUserId;
        FireAlarmMeetingUserDO meetingUser = actualLocalUserId == null ? null
                : fireAlarmMeetingUserRepository.getByLocalUserId(actualLocalUserId,
                fireAlarmUserResolver.resolveUserTenantId(actualLocalUserId));
        if (required && meetingUser == null) {
            throw new BizException("当前用户未配置会议账号映射");
        }
        return meetingUser;
    }

    private FireBrigadeUserDO resolveUserBrigadeRelation(Long localUserId, String fireBrigadeBizId) {
        List<FireBrigadeUserDO> relations = fireBrigadeUserRepository.listByUserId(localUserId);
        if (relations == null || relations.isEmpty()) {
            return null;
        }
        if (StrUtil.isBlank(fireBrigadeBizId)) {
            return relations.get(0);
        }
        return relations.stream()
                .filter(item -> fireBrigadeBizId.trim().equals(item.getFireBrigadeId()))
                .findFirst()
                .orElse(null);
    }

    private VideoPlatformOnlineRoomCO findOnlineFixedRoom(FixedRoomContext context) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("pageNumber", 1);
        requestBody.put("pageSize", 100);
        Map<String, Object> response = hstVideoPlatformClient.onlineRoom(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "查询企业当前会议");
        List<VideoPlatformOnlineRoomCO> rooms = buildOnlineRoomRecords(castMap(response.get("data")).get("records"));
        for (VideoPlatformOnlineRoomCO room : rooms) {
            if (sameRoom(context, room.getRoomId())) {
                return room;
            }
        }
        return null;
    }

    private List<VideoPlatformMeetingParticipantCO> buildParticipants(FixedRoomContext context, Long currentLocalUserId) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("roomId", toNumericOrText(context.roomId));
        requestBody.put("pageNumber", 1);
        requestBody.put("pageSize", 200);
        Map<String, Object> response;
        try {
            response = hstVideoPlatformClient.participant(requestBody);
            hstVideoPlatformClient.ensureSuccess(response, "获取会议当前用户列表");
        }
        catch (BizException ex) {
            if (isRoomNotOnline(ex.getMessage())) {
                return Collections.emptyList();
            }
            throw ex;
        }

        Map<String, FireAlarmMeetingUserDO> meetingUserMap = buildMeetingUserMap();
        List<String> thirdUserNames = extractUserNames(response.get("data"));
        List<VideoPlatformMeetingParticipantCO> result = new ArrayList<>();
        for (String thirdUserName : thirdUserNames) {
            FireAlarmMeetingUserDO meetingUser = meetingUserMap.get(normalize(thirdUserName));
            VideoPlatformMeetingParticipantCO co = new VideoPlatformMeetingParticipantCO();
            co.setThirdUserName(thirdUserName);
            co.setLocalUserId(meetingUser == null ? null : meetingUser.getLocalUserId());
            co.setLocalUserName(meetingUser == null ? null : meetingUser.getLocalUserName());
            co.setLocalPhone(meetingUser == null ? null : meetingUser.getLocalPhone());
            co.setFireBrigadeId(meetingUser == null ? null : meetingUser.getFireBrigadeId());
            co.setFireBrigadeName(meetingUser == null ? null : meetingUser.getFireBrigadeName());
            co.setUserType(meetingUser == null ? null : meetingUser.getUserType());
            co.setUserTypeName(userTypeName(meetingUser == null ? null : meetingUser.getUserType()));
            co.setCurrentUser(currentLocalUserId != null && Objects.equals(currentLocalUserId, co.getLocalUserId()));
            result.add(co);
        }
        return result;
    }

    private void fillMeetingCapacity(VideoPlatformMeetingAccessCheckCO co, List<VideoPlatformMeetingParticipantCO> participants) {
        List<VideoPlatformMeetingParticipantCO> safeParticipants = participants == null ? Collections.emptyList() : participants;
        co.setTotalCount(safeParticipants.size());
        co.setMaxTotalCount(MAX_MEETING_TOTAL_COUNT);
    }

    private boolean isUserInMeeting(List<VideoPlatformMeetingParticipantCO> participants, Long localUserId) {
        if (participants == null || localUserId == null) {
            return false;
        }
        return participants.stream().anyMatch(item -> Objects.equals(localUserId, item.getLocalUserId()));
    }

    private String userTypeName(Integer userType) {
        if (Integer.valueOf(1).equals(userType)) {
            return "接警人";
        }
        if (Integer.valueOf(2).equals(userType)) {
            return "报警人";
        }
        return null;
    }

    private List<VideoPlatformMeetingLogCO> buildRoomLogs(FixedRoomContext context, VideoPlatformMeetingMemberQry qry) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("departId", resolveRoomLogDepartId(qry));
        requestBody.put("startTime", selectText(qry.getStartTime(), TIME_FORMATTER.format(LocalDate.now().atStartOfDay())));
        requestBody.put("endTime", selectText(qry.getEndTime(), TIME_FORMATTER.format(LocalDateTime.now())));
        Map<String, Object> response = hstVideoPlatformClient.roomLog(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "查询会议室参会人信息");

        Map<String, FireAlarmMeetingUserDO> meetingUserMap = buildMeetingUserMap();
        List<Map<String, Object>> rows = extractRows(response.get("data"));
        List<VideoPlatformMeetingLogCO> result = new ArrayList<>();
        for (Map<String, Object> row : rows) {
            if (!sameRoom(context, asText(row.get("roomId")))) {
                continue;
            }
            String thirdUserName = selectText(asText(row.get("userName")), asText(row.get("thirdUserName")));
            FireAlarmMeetingUserDO meetingUser = meetingUserMap.get(normalize(thirdUserName));
            VideoPlatformMeetingLogCO co = new VideoPlatformMeetingLogCO();
            co.setRoomId(asText(row.get("roomId")));
            co.setRoomName(asText(row.get("roomName")));
            co.setThirdUserName(thirdUserName);
            co.setLocalUserId(meetingUser == null ? null : meetingUser.getLocalUserId());
            co.setLocalUserName(meetingUser == null ? null : meetingUser.getLocalUserName());
            co.setFireBrigadeId(meetingUser == null ? null : meetingUser.getFireBrigadeId());
            co.setFireBrigadeName(meetingUser == null ? null : meetingUser.getFireBrigadeName());
            co.setEnterTime(asText(row.get("enterTime")));
            co.setLeaveTime(asText(row.get("leaveTime")));
            co.setDuration(asText(row.get("duration")));
            co.setStatus(StrUtil.isBlank(co.getLeaveTime()) ? "IN_ROOM" : "LEFT");
            result.add(co);
        }
        return result;
    }

    private Object resolveRoomLogDepartId(VideoPlatformMeetingMemberQry qry) {
        if (StrUtil.isNotBlank(qry.getDepartId())) {
            return toNumericOrText(qry.getDepartId());
        }
        FireAlarmMeetingUserDO meetingUser = resolveCurrentMeetingUser(currentUserId(), false);
        if (meetingUser != null && StrUtil.isNotBlank(meetingUser.getThirdUserName())) {
            String departId = queryUserDepartId(meetingUser.getThirdUserName());
            if (StrUtil.isNotBlank(departId)) {
                return toNumericOrText(departId);
            }
        }
        return toNumericOrText(StrUtil.blankToDefault(properties.getDefaultDepartId(), "1"));
    }

    private String queryUserDepartId(String thirdUserName) {
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("searchKey", thirdUserName.trim());
        requestBody.put("searchType", "1");
        requestBody.put("pageNumber", 1);
        requestBody.put("pageSize", 20);
        Map<String, Object> response = hstVideoPlatformClient.userList(requestBody);
        hstVideoPlatformClient.ensureSuccess(response, "查询视频平台用户");
        Object data = response.get("data");
        if (!(data instanceof Map)) {
            return null;
        }
        Object records = castMap(data).get("records");
        if (!(records instanceof List)) {
            return null;
        }
        for (Object item : (List<?>) records) {
            if (!(item instanceof Map)) {
                continue;
            }
            Map<String, Object> row = castMap(item);
            if (thirdUserName.trim().equalsIgnoreCase(asText(row.get("userName")))) {
                return asText(row.get("departId"));
            }
        }
        return null;
    }

    private Map<String, FireAlarmMeetingUserDO> buildMeetingUserMap() {
        List<FireAlarmMeetingUserDO> users = fireAlarmMeetingUserRepository.listAll();
        if (users == null || users.isEmpty()) {
            return Collections.emptyMap();
        }
        return users.stream()
                .filter(item -> StrUtil.isNotBlank(item.getThirdUserName()))
                .collect(Collectors.toMap(item -> normalize(item.getThirdUserName()), item -> item, (first, second) -> first));
    }

    private List<String> extractUserNames(Object data) {
        Object records = extractRecords(data);
        if (!(records instanceof List)) {
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<>();
        for (Object item : (List<?>) records) {
            String userName;
            if (item instanceof Map) {
                Map<String, Object> row = castMap(item);
                userName = selectText(asText(row.get("userName")), selectText(asText(row.get("thirdUserName")), asText(row.get("name"))));
            }
            else {
                userName = asText(item);
            }
            if (StrUtil.isNotBlank(userName)) {
                result.add(userName.trim());
            }
        }
        return result;
    }

    private List<Map<String, Object>> extractRows(Object data) {
        Object records = extractRecords(data);
        if (!(records instanceof List)) {
            return Collections.emptyList();
        }
        List<Map<String, Object>> result = new ArrayList<>();
        for (Object item : (List<?>) records) {
            if (item instanceof Map) {
                result.add(castMap(item));
            }
        }
        return result;
    }

    private Object extractRecords(Object data) {
        if (data instanceof List) {
            return data;
        }
        if (!(data instanceof Map)) {
            return null;
        }
        Map<String, Object> map = castMap(data);
        Object records = map.get("records");
        if (records == null) {
            records = map.get("data");
        }
        if (records == null) {
            records = map.get("list");
        }
        return records;
    }

    private List<String> safeGetLoginAddr() {
        try {
            return getLoginAddr();
        }
        catch (Exception ex) {
            return Collections.emptyList();
        }
    }

    private List<VideoPlatformOnlineRoomCO> buildOnlineRoomRecords(Object records) {
        if (!(records instanceof List)) {
            return Collections.emptyList();
        }
        List<VideoPlatformOnlineRoomCO> result = new ArrayList<>();
        for (Object item : (List<?>) records) {
            Map<String, Object> row = castMap(item);
            VideoPlatformOnlineRoomCO co = new VideoPlatformOnlineRoomCO();
            co.setRoomId(asText(row.get("roomId")));
            co.setRoomName(asText(row.get("roomName")));
            co.setPassword(asText(row.get("password")));
            co.setCurUserCount(asInteger(row.get("curUserCount")));
            co.setStartTime(asText(row.get("startTime")));
            co.setEndTime(asText(row.get("endTime")));
            co.setStatus(asText(row.get("status")));
            co.setVerifyMode(asText(row.get("verifyMode")));
            co.setMaxUserCount(asInteger(row.get("maxUserCount")));
            result.add(co);
        }
        return result;
    }

    private VideoPlatformResultCO buildResultCO(Map<String, Object> response) {
        VideoPlatformResultCO co = new VideoPlatformResultCO();
        co.setCode(selectText(asText(response.get("code")), "200"));
        co.setMsg(selectText(asText(response.get("msg")), "success"));
        co.setData(response.get("data"));
        return co;
    }

    private boolean sameRoom(FixedRoomContext context, String thirdRoomId) {
        if (StrUtil.isBlank(thirdRoomId)) {
            return false;
        }
        String value = thirdRoomId.trim();
        return value.equals(context.roomId) || value.equals(context.roomNo);
    }

    private String resolveRoomId(FireAlarmMeetingRoomDO room) {
        if (room == null) {
            return properties.getDefaultRoomNo();
        }
        return selectText(room.getThirdRoomId(), selectText(room.getThirdRoomNo(), properties.getDefaultRoomNo()));
    }

    private String resolveRoomNo(FireAlarmMeetingRoomDO room) {
        if (room == null) {
            return properties.getDefaultRoomNo();
        }
        return selectText(room.getThirdRoomNo(), selectText(room.getThirdRoomId(), properties.getDefaultRoomNo()));
    }

    private String resolveRoomName(FireAlarmMeetingRoomDO room) {
        if (room == null || StrUtil.isBlank(room.getRoomName())) {
            return "消防报警固定会议室";
        }
        return room.getRoomName();
    }

    private void putIfNotNull(Map<String, Object> target, String key, Object value) {
        if (value != null) {
            target.put(key, value);
        }
    }

    private void putIfNotBlank(Map<String, Object> target, String key, String value) {
        if (StrUtil.isNotBlank(value)) {
            target.put(key, value.trim());
        }
    }

    private Map<String, Object> castMap(Object value) {
        if (!(value instanceof Map)) {
            return new HashMap<>();
        }
        return (Map<String, Object>) value;
    }

    private String asText(Object value) {
        return value == null ? null : String.valueOf(value);
    }

    private Integer asInteger(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        try {
            return Integer.parseInt(String.valueOf(value));
        }
        catch (Exception ex) {
            return null;
        }
    }

    private Object toNumericOrText(String value) {
        if (StrUtil.isBlank(value)) {
            return value;
        }
        if (StrUtil.isNumeric(value)) {
            try {
                return Long.parseLong(value);
            }
            catch (Exception ex) {
                return value;
            }
        }
        return value;
    }

    private String selectText(String preferred, String fallback) {
        return StrUtil.isNotBlank(preferred) ? preferred : fallback;
    }

    private String normalize(String value) {
        return StrUtil.blankToDefault(value, "").trim().toLowerCase(Locale.ROOT);
    }

    private boolean isRoomNotOnline(String message) {
        String msg = StrUtil.blankToDefault(message, "").toLowerCase(Locale.ROOT);
        return msg.contains("不存在") || msg.contains("未开始") || msg.contains("未在线")
                || msg.contains("not exist") || msg.contains("not found") || msg.contains("offline");
    }

    private static class FixedRoomContext {
        private FireAlarmMeetingRoomDO room;
        private String roomCode;
        private String roomId;
        private String roomNo;
        private String roomName;
        private String roomPassword;
    }
}
