package com.zcloud.zcGbsServicer.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbscommon.zclouduser.response.ZcloudUserCo;
import com.zcloud.zcGbsServicer.api.FireBrigadeMemberServiceI;
import com.zcloud.zcGbsServicer.command.convertor.FireBrigadeMemberCoConvertor;
import com.zcloud.zcGbsServicer.dto.FireBrigadeMemberAddCmd;
import com.zcloud.zcGbsServicer.dto.FireBrigadeMemberPageQry;
import com.zcloud.zcGbsServicer.dto.FireBrigadeMemberUpdateCmd;
import com.zcloud.zcGbsServicer.dto.FireBrigadeScreenMemberQry;
import com.zcloud.zcGbsServicer.dto.clientobject.FireBrigadeMemberCO;
import com.zcloud.zcGbsServicer.dto.clientobject.FireBrigadeMemberStatusCO;
import com.zcloud.zcGbsServicer.dto.clientobject.FireBrigadeScreenMemberCO;
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.FireBrigadeScreenMemberRow;
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 org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class FireBrigadeMemberServiceImpl extends FireAlarmServiceSupport implements FireBrigadeMemberServiceI {

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

    @Override
    public SingleResponse<FireBrigadeMemberStatusCO> currentMemberStatus() {
        Long userId = currentUserId();
        FireBrigadeMemberStatusCO co = new FireBrigadeMemberStatusCO();
        co.setUserId(userId);
        if (userId == null) {
            co.setFireBrigadeMember(false);
            return SingleResponse.of(co);
        }
        List<FireBrigadeUserDO> relations = fireBrigadeUserRepository.listByUserId(userId);
        co.setFireBrigadeMember(relations != null && !relations.isEmpty());
        return SingleResponse.of(co);
    }

    @Override
    public PageResponse<FireBrigadeScreenMemberCO> listScreenMembers(FireBrigadeScreenMemberQry qry) {
        FireBrigadeScreenMemberQry actualQry = qry == null ? new FireBrigadeScreenMemberQry() : qry;
        Long corpinfoId = actualQry.getCorpinfoId();
        Integer portArea = actualQry.getPortArea();
        if (portArea != null && portArea != 1 && portArea != 2 && portArea != 3) {
            throw new BizException("港区参数无效");
        }
        int pageIndex = actualQry.getPageIndex() <= 0 ? 1 : actualQry.getPageIndex();
        int pageSize = actualQry.getPageSize() <= 0 ? 10 : actualQry.getPageSize();
        PageResponse<FireBrigadeScreenMemberRow> page = fireBrigadeUserRepository.listScreenMembers(corpinfoId, portArea, pageIndex, pageSize);
        List<FireBrigadeScreenMemberCO> data = page.getData() == null ? Collections.emptyList() : page.getData().stream()
                .map(this::toScreenMemberCO)
                .collect(Collectors.toList());
        return PageResponse.of(data, page.getTotalCount(), page.getPageSize(), page.getPageIndex());
    }

    private FireBrigadeScreenMemberCO toScreenMemberCO(FireBrigadeScreenMemberRow row) {
        FireBrigadeScreenMemberCO co = new FireBrigadeScreenMemberCO();
        if (row == null) {
            return co;
        }
        co.setUserId(row.getUserId());
        co.setUserName(row.getUserName());
        co.setAge(row.getAge());
        co.setBrigadeName(row.getBrigadeName());
        co.setPhone(row.getPhone());
        return co;
    }

    @Override
    public PageResponse<FireBrigadeMemberCO> listPage(FireBrigadeMemberPageQry qry) {
        FireBrigadeDO brigade = getBrigade(qry.getFireBrigadeId());
        List<FireBrigadeUserDO> relations = fireBrigadeUserRepository.listByFireBrigadeId(brigade.getFireBrigadeId());
        Map<Long, ZcloudUserCo> userMap = getUserMap(relations);
        List<FireBrigadeMemberCO> data = relations
                .stream()
                .map(item -> buildCO(item, brigade, userMap))
                .filter(item -> StrUtil.isBlank(qry.getName()) || StrUtil.containsIgnoreCase(item.getName(), qry.getName().trim()))
                .collect(Collectors.toList());
        int pageSize = qry.getPageSize() <= 0 ? data.size() : qry.getPageSize();
        int pageIndex = qry.getPageIndex() <= 0 ? 1 : qry.getPageIndex();
        int fromIndex = Math.min((pageIndex - 1) * pageSize, data.size());
        int toIndex = Math.min(fromIndex + pageSize, data.size());
        return PageResponse.of(data.subList(fromIndex, toIndex), data.size(), pageSize, pageIndex);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<FireBrigadeMemberCO> add(FireBrigadeMemberAddCmd cmd) {
        FireBrigadeDO brigade = getBrigade(cmd.getFireBrigadeId());
        Long userId = parseUserId(cmd.getUserId());
        if (userId == null) {
            throw new BizException("用户ID不能为空");
        }
        if (fireBrigadeUserRepository.existsRelation(brigade.getFireBrigadeId(), userId, null)) {
            throw new BizException("消防队员已存在");
        }
        prepareMemberRelation(userId, brigade.getFireBrigadeId(), null);
        FireBrigadeUserDO relation = new FireBrigadeUserDO();
        relation.setId(nextId());
        relation.setFireBrigadeUserId(nextBizId());
        relation.setFireBrigadeId(brigade.getFireBrigadeId());
        relation.setUserId(userId);
        fillMemberSnapshot(relation, cmd);
        fillCreateAudit(relation);
        syncCreateMeetingUser(relation, brigade);
        fireBrigadeUserRepository.save(relation);
        return SingleResponse.of(buildCO(relation, brigade, fireAlarmUserResolver.getUserMap(Collections.singleton(userId))));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(FireBrigadeMemberUpdateCmd cmd) {
        FireBrigadeUserDO relation = fireBrigadeUserRepository.getById(cmd.getId());
        if (relation == null) {
            throw new BizException("消防队员不存在");
        }
        FireBrigadeDO brigade = getBrigade(cmd.getFireBrigadeId());
        Long userId = parseUserId(cmd.getUserId());
        if (userId == null) {
            throw new BizException("用户ID不能为空");
        }
        if (fireBrigadeUserRepository.existsRelation(brigade.getFireBrigadeId(), userId, relation.getId())) {
            throw new BizException("消防队员已存在");
        }
        prepareMemberRelation(userId, brigade.getFireBrigadeId(), relation.getId());
        relation.setFireBrigadeId(brigade.getFireBrigadeId());
        relation.setUserId(userId);
        fillMemberSnapshot(relation, cmd);
        fillUpdateAudit(relation);
        fireBrigadeUserRepository.updateById(relation);
        syncCreateMeetingUser(relation, brigade);
    }

    @Override
    public FireBrigadeMemberCO get(Long id) {
        FireBrigadeUserDO relation = fireBrigadeUserRepository.getById(id);
        if (relation == null) {
            throw new BizException("消防队员不存在");
        }
        FireBrigadeDO brigade = fireBrigadeRepository.getByFireBrigadeId(relation.getFireBrigadeId());
        FireBrigadeDO actualBrigade = brigade == null ? new FireBrigadeDO() : brigade;
        if (actualBrigade.getFireBrigadeId() == null) {
            actualBrigade.setId(null);
            actualBrigade.setFireBrigadeId(relation.getFireBrigadeId());
        }
        return buildCO(relation, actualBrigade, fireAlarmUserResolver.getUserMap(Collections.singleton(relation.getUserId())));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(Long id) {
        FireBrigadeUserDO relation = fireBrigadeUserRepository.getById(id);
        if (relation == null) {
            throw new BizException("消防队员不存在");
        }
        checkLeaderRelation(relation);
        fireBrigadeUserRepository.removeById(id);
        syncDeleteMeetingUserIfUnused(relation);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeBatch(Long[] ids) {
        if (ids == null || ids.length == 0) {
            return;
        }
        for (Long id : ids) {
            FireBrigadeUserDO relation = fireBrigadeUserRepository.getById(id);
            if (relation != null) {
                checkLeaderRelation(relation);
            }
        }
        for (Long id : ids) {
            FireBrigadeUserDO relation = fireBrigadeUserRepository.getById(id);
            if (relation != null) {
                fireBrigadeUserRepository.removeById(id);
                syncDeleteMeetingUserIfUnused(relation);
            }
        }
    }

    private FireBrigadeDO getBrigade(Long id) {
        FireBrigadeDO brigade = fireBrigadeRepository.getById(id);
        if (brigade == null) {
            throw new BizException("消防队伍不存在");
        }
        return brigade;
    }

    private void checkLeaderRelation(FireBrigadeUserDO relation) {
        FireBrigadeDO brigade = fireBrigadeRepository.getByFireBrigadeId(relation.getFireBrigadeId());
        if (brigade != null && brigade.getLeaderUserId() != null && brigade.getLeaderUserId().equals(relation.getUserId())) {
            throw new BizException("负责人未更换前，不能删除该成员");
        }
    }

    private FireBrigadeMemberCO buildCO(FireBrigadeUserDO relation, FireBrigadeDO brigade, Map<Long, ZcloudUserCo> userMap) {
        FireBrigadeMemberCO co = fireBrigadeMemberCoConvertor.converDOToCO(relation);
        ZcloudUserCo user = relation.getUserId() == null ? null : userMap.get(relation.getUserId());
        co.setFireBrigadeId(brigade.getId());
        co.setUserId(relation.getUserId() == null ? null : String.valueOf(relation.getUserId()));
        co.setDutyId(relation.getDutyId());
        co.setCorpId(relation.getCorpId());
        co.setGender(relation.getGender());
        co.setAge(relation.getAge());
        co.setName(selectText(relation.getUserName(), user == null ? null : user.getName()));
        co.setIdNo(selectText(relation.getIdNo(), user == null ? null : user.getUserIdCard()));
        co.setPhone(selectText(relation.getPhone(), user == null ? null : user.getPhone()));
        co.setDuty(selectText(relation.getDutyName(), user == null ? null : user.getPostName()));
        co.setOrg(selectText(relation.getOrgName(), user == null ? null : user.getDepartmentName()));
        if (brigade.getLeaderUserId() != null && brigade.getLeaderUserId().equals(relation.getUserId())) {
            co.setRoleCode(1);
        }
        return co;
    }

    private void fillMemberSnapshot(FireBrigadeUserDO relation, FireBrigadeMemberAddCmd cmd) {
        relation.setUserName(cmd.getName());
        relation.setGender(cmd.getGender());
        relation.setIdNo(cmd.getIdNo());
        relation.setPhone(cmd.getPhone());
        relation.setDutyId(cmd.getDutyId());
        relation.setDutyName(cmd.getDuty());
        relation.setCorpId(cmd.getCorpId());
        relation.setOrgName(cmd.getOrg());
        relation.setAge(cmd.getAge());
    }

    /**
     * 用户换队时软删除其他队伍的有效成员关系。
     */
    private void prepareMemberRelation(Long userId, String targetFireBrigadeId, Long currentRelationId) {
        List<FireBrigadeUserDO> activeRelations = fireBrigadeUserRepository.listByUserId(userId);
        if (activeRelations == null) {
            return;
        }
        for (FireBrigadeUserDO rel : activeRelations) {
            if (currentRelationId != null && currentRelationId.equals(rel.getId())) {
                continue;
            }
            if (!targetFireBrigadeId.equals(rel.getFireBrigadeId())) {
                fireBrigadeUserRepository.removeById(rel.getId());
            }
        }
    }

    private void syncCreateMeetingUser(FireBrigadeUserDO relation, FireBrigadeDO brigade) {
        if (StrUtil.isBlank(hstVideoPlatformProperties.getDefaultUserPassword())) {
            throw new BizException("视频平台默认用户密码未配置，不能创建消防会议账号");
        }
        String thirdUserName = resolveThirdUserName(relation.getUserId());
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("userName", thirdUserName);
        requestBody.put("password", hstVideoPlatformProperties.getDefaultUserPassword());
        requestBody.put("passwordType", 0);
        requestBody.put("mobile", relation.getPhone());
        requestBody.put("nickName", selectText(relation.getUserName(), thirdUserName));
        requestBody.put("sex", "女".equals(relation.getGender()) ? "1" : "0");
        requestBody.put("departId", StrUtil.blankToDefault(brigade.getThirdDepartId(), "0"));
        requestBody.put("adminRole", 2);

        Map<String, Object> data = new HashMap<>();
        String syncMsg = "第三方账号创建成功";
        try {
            Map<String, Object> response = hstVideoPlatformClient.addUser(requestBody);
            hstVideoPlatformClient.ensureSuccess(response, "创建视频平台用户");
            data = castMap(response.get("data"));
            syncMsg = selectText(asText(response.get("msg")), syncMsg);
        }
        catch (BizException ex) {
            if (!isAlreadyExists(ex.getMessage())) {
                throw new BizException("创建视频平台用户失败：" + ex.getMessage());
            }
            syncMsg = "第三方账号已存在，已复用";
        }

        upsertMeetingUserMapping(relation, brigade, thirdUserName, data, syncMsg);
        syncRoomAuth(thirdUserName, 2);
    }

    private void upsertMeetingUserMapping(FireBrigadeUserDO relation, FireBrigadeDO brigade,
                                          String thirdUserName, Map<String, Object> data, String syncMsg) {
        Long userTenantId = fireAlarmUserResolver.resolveUserTenantId(relation.getUserId());
        FireAlarmMeetingUserDO meetingUser = fireAlarmMeetingUserRepository.getByLocalUserId(relation.getUserId(), userTenantId);
        boolean isNew = meetingUser == null;
        if (meetingUser == null) {
            meetingUser = new FireAlarmMeetingUserDO();
            meetingUser.setId(nextId());
            meetingUser.setFireAlarmMeetingUserId(nextBizId());
            fillCreateAudit(meetingUser);
        }
        else {
            fillUpdateAudit(meetingUser);
        }
        fireAlarmUserResolver.applyLocalUserTenant(meetingUser, relation.getUserId());

        FireAlarmMeetingRoomDO room = fireAlarmMeetingRoomRepository.getByRoomCode(hstVideoPlatformProperties.getDefaultRoomCode());
        LocalDateTime now = LocalDateTime.now();
        meetingUser.setLocalUserId(relation.getUserId());
        meetingUser.setLocalUserName(relation.getUserName());
        meetingUser.setLocalPhone(relation.getPhone());
        meetingUser.setUserType(1);
        meetingUser.setFireBrigadeId(brigade.getFireBrigadeId());
        meetingUser.setFireBrigadeName(brigade.getBrigadeName());
        meetingUser.setThirdUserId(selectText(asText(data.get("userId")), meetingUser.getThirdUserId()));
        meetingUser.setThirdUserName(selectText(asText(data.get("userName")), thirdUserName));
        meetingUser.setThirdNickName(selectText(asText(data.get("displayName")), relation.getUserName()));
        meetingUser.setThirdDepartId(selectText(asText(data.get("departId")), brigade.getThirdDepartId()));
        meetingUser.setThirdDepartName(brigade.getThirdDepartName());
        meetingUser.setAuthRoomId(room == null ? hstVideoPlatformProperties.getDefaultRoomNo() : selectText(room.getThirdRoomId(), room.getThirdRoomNo()));
        meetingUser.setAuthRoomName(room == null ? "消防报警固定会议室" : room.getRoomName());
        meetingUser.setAuthStatus(1);
        meetingUser.setAuthTime(now);
        meetingUser.setSyncStatus(1);
        meetingUser.setSyncMsg(syncMsg);
        meetingUser.setLastSyncTime(now);
        if (isNew) {
            fireAlarmMeetingUserRepository.save(meetingUser);
        }
        else {
            fireAlarmMeetingUserRepository.updateById(meetingUser);
        }
    }

    private void syncRoomAuth(String thirdUserName, int permission) {
        if (StrUtil.isBlank(thirdUserName)) {
            return;
        }
        FireAlarmMeetingRoomDO room = fireAlarmMeetingRoomRepository.getByRoomCode(hstVideoPlatformProperties.getDefaultRoomCode());
        String roomId = room == null ? hstVideoPlatformProperties.getDefaultRoomNo()
                : selectText(room.getThirdRoomId(), room.getThirdRoomNo());
        if (StrUtil.isBlank(roomId)) {
            return;
        }
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("roomUserStr", roomId + "," + permission);
        requestBody.put("userName", thirdUserName);
        try {
            Map<String, Object> response = hstVideoPlatformClient.authRoom(requestBody);
            hstVideoPlatformClient.ensureSuccess(response, permission == 0 ? "解除用户会议室授权" : "用户授权会议室");
        } catch (BizException ex) {
            throw new BizException("用户授权会议室失败：" + ex.getMessage());
        }
    }

    private void syncDeleteMeetingUserIfUnused(FireBrigadeUserDO relation) {
        if (relation == null || relation.getUserId() == null) {
            return;
        }
        List<FireBrigadeUserDO> activeRelations = fireBrigadeUserRepository.listByUserId(relation.getUserId());
        if (activeRelations != null && !activeRelations.isEmpty()) {
            return;
        }
        FireAlarmMeetingUserDO meetingUser = fireAlarmMeetingUserRepository.getByLocalUserId(relation.getUserId(),
                fireAlarmUserResolver.resolveUserTenantId(relation.getUserId()));
        if (meetingUser == null) {
            return;
        }
        if (StrUtil.isNotBlank(meetingUser.getThirdUserName())) {
            syncRoomAuth(meetingUser.getThirdUserName(), 0);
            Map<String, Object> requestBody = new LinkedHashMap<>();
            requestBody.put("userName", meetingUser.getThirdUserName());
            try {
                Map<String, Object> response = hstVideoPlatformClient.deleteUser(requestBody);
                hstVideoPlatformClient.ensureSuccess(response, "删除视频平台用户");
            }
            catch (BizException ex) {
                if (!isAlreadyMissing(ex.getMessage())) {
                    throw new BizException("删除视频平台用户失败：" + ex.getMessage());
                }
            }
        }
        fillUpdateAudit(meetingUser);
        meetingUser.setDeleteEnum("TRUE");
        meetingUser.setSyncStatus(1);
        meetingUser.setSyncMsg("第三方账号已删除，会议账号映射已作废");
        meetingUser.setLastSyncTime(LocalDateTime.now());
        fireAlarmMeetingUserRepository.updateById(meetingUser);
    }

    private String resolveThirdUserName(Long localUserId) {
        FireAlarmMeetingUserDO meetingUser = fireAlarmMeetingUserRepository.getByLocalUserId(localUserId,
                fireAlarmUserResolver.resolveUserTenantId(localUserId));
        if (meetingUser != null && StrUtil.isNotBlank(meetingUser.getThirdUserName())) {
            return meetingUser.getThirdUserName().trim();
        }
        String userName = StrUtil.blankToDefault(hstVideoPlatformProperties.getFireUserPrefix(), "xfbj") + localUserId;
        if (userName.length() > 32) {
            return userName.substring(0, 32);
        }
        return userName;
    }

    private boolean isAlreadyExists(String message) {
        String msg = StrUtil.blankToDefault(message, "").toLowerCase();
        return msg.contains("存在") || msg.contains("exist") || msg.contains("duplicate");
    }

    private boolean isAlreadyMissing(String message) {
        String msg = StrUtil.blankToDefault(message, "").toLowerCase();
        return msg.contains("不存在") || msg.contains("not exist") || msg.contains("not found");
    }

    @SuppressWarnings("unchecked")
    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 String selectText(String preferred, String fallback) {
        return StrUtil.isNotBlank(preferred) ? preferred : fallback;
    }

    private Long parseUserId(String userId) {
        if (StrUtil.isBlank(userId)) {
            return null;
        }
        try {
            return Long.valueOf(userId.trim());
        }
        catch (NumberFormatException ex) {
            return null;
        }
    }

    private Map<Long, ZcloudUserCo> getUserMap(List<FireBrigadeUserDO> relations) {
        Set<Long> userIds = relations.stream()
                .map(FireBrigadeUserDO::getUserId)
                .filter(java.util.Objects::nonNull)
                .collect(Collectors.toSet());
        return fireAlarmUserResolver.getUserMap(userIds);
    }

    private long nextId() {
        return IdUtil.getSnowflake(1, 1).nextId();
    }

    private String nextBizId() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}
