package com.microservices.zone.journals.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteFileService;
import com.microservices.system.api.domain.SysFileInfo;
import com.microservices.system.api.domain.SimpleSysUser;
import com.microservices.system.api.utils.FeignUtils;
import com.microservices.zone.common.service.IZoneCommonService;
import com.microservices.zone.journals.domain.ZoneJournals;
import com.microservices.zone.journals.domain.vo.ZoneJournalsChildrenDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsInputVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsParentDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsSearchVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsUpdateVo;
import com.microservices.zone.journals.mapper.ZoneJournalsMapper;
import com.microservices.zone.journals.service.IZoneJournalsService;
import com.microservices.zone.resource.service.IZoneResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 通用评论Service业务层处理
 *
 * @author otto
 * @date 2025-04-30
 */
@Service
public class ZoneJournalsServiceImpl implements IZoneJournalsService {

    @Autowired
    private ZoneJournalsMapper zoneJournalsMapper;

    @Autowired
    private IZoneCommonService zoneCommonService;

    @Autowired
    private IZoneResourceService zoneResourceService;

    @Autowired
    private RemoteFileService remoteFileService;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 查询评论
     *
     * @param id 评论主键
     * @return 评论
     */
    @Override
    public ZoneJournals selectZoneJournalsById(Long id) {
        ZoneJournals zoneJournals = zoneJournalsMapper.selectZoneJournalsById(id);
        if (zoneJournals == null) {
            throw new ServiceException("该评论不存在(评论Id[%d])", id);
        }
        // 校验评论对象是否存在
        checkByType(zoneJournals);
        return zoneJournals;
    }

    /**
     * 查询评论列表（二级结构）
     *
     * @param searchVo 查询条件
     * @return 评论列表（父评论包含子评论）
     */
    @Override
    public List<ZoneJournalsParentDataVo> selectJournalsList(ZoneJournalsSearchVo searchVo) {
        // 校验评论对象是否存在
        ZoneJournals checkJournal = new ZoneJournals();
        checkJournal.setJournalizedId(searchVo.getJournalized_id());
        checkJournal.setJournalizedType(searchVo.getJournalized_type());
        checkByType(checkJournal);

        // 查询父评论列表
        ZoneJournals query = new ZoneJournals();
        query.setJournalizedId(searchVo.getJournalized_id());
        query.setJournalizedType(searchVo.getJournalized_type());
        query.setUserId(searchVo.getUser_id());
        query.setPrivateNotes(searchVo.getPrivate_notes());
        List<ZoneJournals> parentJournalsList = zoneJournalsMapper.selectZoneJournalsList(query);

        // 转换为VO并组装子评论
        return parentJournalsList.stream()
                .map(this::toParentDataVo)
                .collect(Collectors.toList());
    }

    /**
     * 新增评论
     *
     * @param inputVo 评论输入信息
     * @return 评论ID
     */
    @Override
    public Long insertJournals(ZoneJournalsInputVo inputVo) {
        // 校验评论对象类型
        ZoneJournals zoneJournals = new ZoneJournals();
        zoneJournals.setJournalizedId(inputVo.getJournalized_id());
        zoneJournals.setJournalizedType(inputVo.getJournalized_type());
        checkByType(zoneJournals);

        // 如果是子评论，校验父评论
        if (inputVo.getParent_id() != null) {
            ZoneJournals parentJournal = selectZoneJournalsById(inputVo.getParent_id());
            if (parentJournal.getParentId() != null) {
                throw new ServiceException("该评论非父级评论(评论ID[%d])", parentJournal.getId());
            }
            if (!parentJournal.getJournalizedType().equals(inputVo.getJournalized_type())) {
                throw new ServiceException("当前父级评论与当前评论类型不相同");
            }
            if (!parentJournal.getJournalizedId().equals(inputVo.getJournalized_id())) {
                throw new ServiceException("当前父级评论与当前评论对象不相同");
            }
        }

        // 如果有回复评论，校验回复评论
        if (inputVo.getReply_id() != null) {
            if (inputVo.getParent_id() == null) {
                throw new ServiceException("一级评论不能进行回复操作");
            }
            ZoneJournals replyJournal = selectZoneJournalsById(inputVo.getReply_id());
            if (!replyJournal.getJournalizedType().equals(inputVo.getJournalized_type())) {
                throw new ServiceException("当前回复评论与当前评论类型不相同");
            }
            if (!replyJournal.getJournalizedId().equals(inputVo.getJournalized_id())) {
                throw new ServiceException("当前回复评论与当前评论对象不相同");
            }
        }

        // 校验附件列表
        checkAttachments(inputVo.getAttachment_identifiers());

        // 构建评论对象
        ZoneJournals newJournal = new ZoneJournals();
        newJournal.setJournalizedId(inputVo.getJournalized_id());
        newJournal.setJournalizedType(inputVo.getJournalized_type());
        newJournal.setParentId(inputVo.getParent_id());
        newJournal.setReplyId(inputVo.getReply_id());
        newJournal.setNotes(inputVo.getNotes());
        newJournal.setPrivateNotes(StringUtils.isEmpty(inputVo.getPrivate_notes()) ? "0" : inputVo.getPrivate_notes());
        newJournal.setAttachmentIdentifiers(inputVo.getAttachment_identifiers());

        // 处理@用户列表
        if (inputVo.getReceivers_login() != null && inputVo.getReceivers_login().length > 0) {
            try {
                newJournal.setReceiversLogin(objectMapper.writeValueAsString(inputVo.getReceivers_login()));
            } catch (Exception e) {
                throw new ServiceException("@用户列表格式错误");
            }
        }

        // 获取当前用户ID
        Long userId = SecurityUtils.getUserId();
        if (userId == null || userId == 0) {
            throw new ServiceException("无法获取当前用户信息");
        }
        newJournal.setUserId(userId);
        newJournal.setCreateBy(SecurityUtils.getUsername());
        newJournal.setCreatedOn(DateUtils.getNowDate());

        // 插入评论
        zoneJournalsMapper.insertZoneJournals(newJournal);

        // 如果是子评论，更新父评论的子评论数量
        if (inputVo.getParent_id() != null) {
            zoneJournalsMapper.updateCommentsCountById(inputVo.getParent_id());
        }

        return newJournal.getId();
    }

    /**
     * 修改评论
     *
     * @param updateVo 评论更新信息
     * @return 结果
     */
    @Override
    public int updateJournals(ZoneJournalsUpdateVo updateVo) {
        // 查询原评论并校验
        ZoneJournals oldJournal = selectZoneJournalsById(updateVo.getId());

        // 校验是否为评论创建者
        Long userId = SecurityUtils.getUserId();
        if (!oldJournal.getUserId().equals(userId)) {
            throw new ServiceException("只能修改自己的评论");
        }

        // 校验附件列表
        checkAttachments(updateVo.getAttachment_identifiers());

        // 构建更新对象
        ZoneJournals updateJournal = new ZoneJournals();
        updateJournal.setId(updateVo.getId());
        updateJournal.setNotes(updateVo.getNotes());
        updateJournal.setAttachmentIdentifiers(updateVo.getAttachment_identifiers());

        // 处理@用户列表
        if (updateVo.getReceivers_login() != null && updateVo.getReceivers_login().length > 0) {
            try {
                updateJournal.setReceiversLogin(objectMapper.writeValueAsString(updateVo.getReceivers_login()));
            } catch (Exception e) {
                throw new ServiceException("@用户列表格式错误");
            }
        }

        updateJournal.setUpdateBy(SecurityUtils.getUsername());
        updateJournal.setUpdatedOn(DateUtils.getNowDate());

        return zoneJournalsMapper.updateZoneJournals(updateJournal);
    }

    /**
     * 删除评论（级联删除子评论）
     *
     * @param id 评论主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteJournalsById(Long id) {
        ZoneJournals zoneJournals = selectZoneJournalsById(id);

        // 校验是否为评论创建者
        Long userId = SecurityUtils.getUserId();
        if (!zoneJournals.getUserId().equals(userId)) {
            throw new ServiceException("只能删除自己的评论");
        }

        // 如果是父级评论，先删除所有子评论
        if (zoneJournals.getParentId() == null) {
            List<ZoneJournals> childrenJournalsList = zoneJournalsMapper.selectChildrenZoneJournalsList(zoneJournals.getId());
            for (ZoneJournals childrenJournal : childrenJournalsList) {
                zoneJournalsMapper.deleteZoneJournalsById(childrenJournal.getId());
            }
        }

        return zoneJournalsMapper.deleteZoneJournalsById(id);
    }

    /**
     * 批量删除评论
     *
     * @param ids 需要删除的评论主键集合
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteJournalsByIds(Long[] ids) {
        int success = 0;
        for (Long id : ids) {
            success += deleteJournalsById(id);
        }
        return success;
    }

    /**
     * 根据类型和评论对象Id删除所有评论
     *
     * @param type      类型
     * @param journalId 评论对象Id
     */
    @Override
    public void deleteJournalsByTypeAndJournalId(String type, Long journalId) {
        zoneJournalsMapper.deleteZoneJournalsByTypeAndJournalId(type, journalId);
    }

    /**
     * 批量更新关联ID
     *
     * @param type         类型
     * @param oldJournalId 旧评论对象Id
     * @param journalId    新评论对象Id
     */
    @Override
    public void updateJournalsIdByTypeAndOldId(String type, Long oldJournalId, Long journalId) {
        zoneJournalsMapper.updateJournalsIdByTypeAndOldId(type, oldJournalId, journalId);
    }

    /**
     * 根据类型校验评论对象是否存在
     *
     * @param zoneJournals 评论对象
     */
    private void checkByType(ZoneJournals zoneJournals) {
        switch (zoneJournals.getJournalizedType()) {
            case ZoneJournals.JOURNAL_TYPE_AI_SKILL:
            case ZoneJournals.JOURNAL_TYPE_AI_MODEL:
            case ZoneJournals.JOURNAL_TYPE_AI_DATASET:
            case ZoneJournals.JOURNAL_TYPE_ZONE_RESOURCE:
                // 校验资源是否存在
                zoneResourceService.selectZoneResourceById(zoneJournals.getJournalizedId());
                break;
            default:
                throw new ServiceException("当前不支持该类型评论(评论类型[%s])", zoneJournals.getJournalizedType());
        }
    }

    /**
     * 校验附件列表
     *
     * @param attachmentIdentifiers 附件标识列表
     */
    private void checkAttachments(String attachmentIdentifiers) {
        if (StringUtils.isEmpty(attachmentIdentifiers)) {
            return;
        }
        try {
            List<SysFileInfo> fileInfos = FeignUtils.getReturnData(
                    remoteFileService.getFileListByIdentifier(attachmentIdentifiers)
            );
            if (CollectionUtils.isEmpty(fileInfos)) {
                throw new ServiceException("附件不存在或已被删除");
            }
        } catch (Exception e) {
            throw new ServiceException("附件校验失败：%s", e.getMessage());
        }
    }

    /**
     * 转换为父评论VO
     *
     * @param zoneJournals 评论实体
     * @return 父评论VO
     */
    private ZoneJournalsParentDataVo toParentDataVo(ZoneJournals zoneJournals) {
        ZoneJournalsParentDataVo parentVo = new ZoneJournalsParentDataVo();
        parentVo.setId(zoneJournals.getId());
        parentVo.setNotes(zoneJournals.getNotes());
        parentVo.setCreated_on(zoneJournals.getCreatedOn());
        parentVo.setUpdated_on(zoneJournals.getUpdatedOn());

        // 获取用户信息
        SimpleSysUser user = zoneCommonService.getSimpleSysUserByUserId(zoneJournals.getUserId());
        parentVo.setUser(user);

        // 获取附件列表
        parentVo.setAttachments(getAttachments(zoneJournals.getAttachmentIdentifiers()));

        // 获取子评论列表
        List<ZoneJournals> childrenJournalsList = zoneJournalsMapper.selectChildrenZoneJournalsList(zoneJournals.getId());
        List<ZoneJournalsChildrenDataVo> childrenVoList = childrenJournalsList.stream()
                .map(this::toChildrenDataVo)
                .collect(Collectors.toList());
        parentVo.setChildren_journals(childrenVoList);
        parentVo.setComments_count(childrenVoList.size());

        return parentVo;
    }

    /**
     * 转换为子评论VO
     *
     * @param zoneJournals 评论实体
     * @return 子评论VO
     */
    private ZoneJournalsChildrenDataVo toChildrenDataVo(ZoneJournals zoneJournals) {
        ZoneJournalsChildrenDataVo childrenVo = new ZoneJournalsChildrenDataVo();
        childrenVo.setId(zoneJournals.getId());
        childrenVo.setNotes(zoneJournals.getNotes());
        childrenVo.setCreated_on(zoneJournals.getCreatedOn());
        childrenVo.setUpdated_on(zoneJournals.getUpdatedOn());

        // 获取用户信息
        SimpleSysUser user = zoneCommonService.getSimpleSysUserByUserId(zoneJournals.getUserId());
        childrenVo.setUser(user);

        // 获取附件列表
        childrenVo.setAttachments(getAttachments(zoneJournals.getAttachmentIdentifiers()));

        // 获取回复用户信息
        if (zoneJournals.getReplyId() != null) {
            ZoneJournals replyJournal = zoneJournalsMapper.selectZoneJournalsById(zoneJournals.getReplyId());
            if (replyJournal != null) {
                SimpleSysUser replyUser = zoneCommonService.getSimpleSysUserByUserId(replyJournal.getUserId());
                childrenVo.setReply_user(replyUser);
            }
        }

        return childrenVo;
    }

    /**
     * 获取附件列表
     *
     * @param attachmentIdentifiers 附件标识列表
     * @return 附件列表
     */
    private List<SysFileInfo> getAttachments(String attachmentIdentifiers) {
        if (StringUtils.isEmpty(attachmentIdentifiers)) {
            return new ArrayList<>();
        }
        try {
            List<SysFileInfo> fileInfos = FeignUtils.getReturnData(
                    remoteFileService.getFileListByIdentifier(attachmentIdentifiers)
            );
            return CollectionUtils.isEmpty(fileInfos) ? new ArrayList<>() : fileInfos;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }
}
