package com.yhy.module.core.service.calcbaserate.impl;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_DIRECTORY_HAS_CHILDREN;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_DIRECTORY_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_DIRECTORY_NOT_SAME_LEVEL;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_DIRECTORY_PARENT_NOT_EXISTS;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.yhy.module.core.controller.admin.calcbaserate.vo.CalcBaseRateDirectoryRespVO;
import com.yhy.module.core.controller.admin.calcbaserate.vo.CalcBaseRateDirectorySaveReqVO;
import com.yhy.module.core.controller.admin.calcbaserate.vo.CalcBaseRateDirectorySwapSortReqVO;
import com.yhy.module.core.dal.dataobject.calcbaserate.CalcBaseRateDirectoryDO;
import com.yhy.module.core.dal.dataobject.calcbaserate.CalcBaseRateItemDO;
import com.yhy.module.core.dal.mysql.calcbaserate.CalcBaseRateDirectoryMapper;
import com.yhy.module.core.dal.mysql.calcbaserate.CalcBaseRateItemMapper;
import com.yhy.module.core.service.calcbaserate.CalcBaseRateDirectoryService;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

/**
 * 基数费率目录 Service 实现类
 *
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class CalcBaseRateDirectoryServiceImpl implements CalcBaseRateDirectoryService {

    @Resource
    private CalcBaseRateDirectoryMapper calcBaseRateDirectoryMapper;

    @Resource
    private CalcBaseRateItemMapper calcBaseRateItemMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCalcBaseRateDirectory(CalcBaseRateDirectorySaveReqVO createReqVO) {
        // 1. 构建DO对象
        CalcBaseRateDirectoryDO directory = buildCalcBaseRateDirectoryDO(createReqVO);

        // 2. 设置排序
        if (directory.getSortOrder() == null) {
            directory.setSortOrder(getNextSortOrder(createReqVO.getParentId(), createReqVO.getCalcBaseRateCatalogId()));
        }

        // 3. 预设level和临时path
        CalcBaseRateDirectoryDO parent = null;
        if (createReqVO.getParentId() != null) {
            parent = calcBaseRateDirectoryMapper.selectById(createReqVO.getParentId());
            if (parent == null) {
                throw exception(CALC_BASE_RATE_DIRECTORY_PARENT_NOT_EXISTS);
            }
            directory.setLevel(parent.getLevel() + 1);
            directory.setPath(parent.getPath());
        } else {
            directory.setLevel(1);
            directory.setPath(new String[]{});
        }

        // 4. 插入数据库获取ID
        calcBaseRateDirectoryMapper.insert(directory);

        // 5. 更新真实路径
        String[] realPath = parent == null
            ? new String[]{directory.getId().toString()}
            : ArrayUtil.append(parent.getPath(), directory.getId().toString());
        directory.setPath(realPath);
        calcBaseRateDirectoryMapper.updateById(directory);

        return directory.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCalcBaseRateDirectory(CalcBaseRateDirectorySaveReqVO updateReqVO) {
        // 1. 校验节点存在
        validateCalcBaseRateDirectoryExists(updateReqVO.getId());

        // 2. 构建更新对象
        CalcBaseRateDirectoryDO updateObj = CalcBaseRateDirectoryDO.builder()
                .id(updateReqVO.getId())
                .name(updateReqVO.getName())
                .sortOrder(updateReqVO.getSortOrder())
                .attributes(updateReqVO.getAttributes())
                .build();

        // 3. 更新数据库
        calcBaseRateDirectoryMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteCalcBaseRateDirectory(Long id) {
        // 1. 校验节点存在
        validateCalcBaseRateDirectoryExists(id);

        // 2. 校验是否有子节点
        List<CalcBaseRateDirectoryDO> children = calcBaseRateDirectoryMapper.selectListByParentId(id);
        if (CollUtil.isNotEmpty(children)) {
            throw exception(CALC_BASE_RATE_DIRECTORY_HAS_CHILDREN);
        }

        // 3. 删除节点
        calcBaseRateDirectoryMapper.deleteById(id);
    }

    @Override
    public CalcBaseRateDirectoryRespVO getCalcBaseRateDirectory(Long id) {
        CalcBaseRateDirectoryDO item = calcBaseRateDirectoryMapper.selectById(id);
        if (item == null) {
            return null;
        }
        return convertToRespVO(item);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<CalcBaseRateDirectoryRespVO> getCalcBaseRateDirectoryTree(Long calcBaseRateCatalogId) {
        // 1. 查询所有节点
        List<CalcBaseRateDirectoryDO> allItems = calcBaseRateDirectoryMapper.selectListByCalcBaseRateCatalogId(calcBaseRateCatalogId);

        // 2. 查找所有parentId为null的节点
        List<CalcBaseRateDirectoryDO> rootNodes = allItems.stream()
                .filter(item -> item.getParentId() == null)
                .sorted((a, b) -> {
                    Integer sortA = a.getSortOrder() != null ? a.getSortOrder() : Integer.MAX_VALUE;
                    Integer sortB = b.getSortOrder() != null ? b.getSortOrder() : Integer.MAX_VALUE;
                    return sortA.compareTo(sortB);
                })
                .collect(Collectors.toList());

        CalcBaseRateDirectoryDO rootNode;

        // 3. 如果没有根节点，自动创建一个
        if (rootNodes.isEmpty()) {
            rootNode = CalcBaseRateDirectoryDO.builder()
                    .calcBaseRateCatalogId(calcBaseRateCatalogId)
                    .parentId(null)
                    .name("根目录")
                    .level(1)
                    .sortOrder(1)
                    .path(new String[]{})
                    .build();
            calcBaseRateDirectoryMapper.insert(rootNode);

            // 更新真实路径
            rootNode.setPath(new String[]{rootNode.getId().toString()});
            calcBaseRateDirectoryMapper.updateById(rootNode);

            allItems.add(rootNode);
        } else {
            rootNode = rootNodes.get(0);

            // 4. 如果存在多个parentId为null的节点，将其他节点挂到根节点下
            if (rootNodes.size() > 1) {
                for (int i = 1; i < rootNodes.size(); i++) {
                    CalcBaseRateDirectoryDO orphanNode = rootNodes.get(i);
                    orphanNode.setParentId(rootNode.getId());
                    orphanNode.setLevel(rootNode.getLevel() + 1);
                    String[] newPath = ArrayUtil.append(rootNode.getPath(), orphanNode.getId().toString());
                    orphanNode.setPath(newPath);
                    calcBaseRateDirectoryMapper.updateById(orphanNode);

                    allItems.stream()
                            .filter(item -> item.getId().equals(orphanNode.getId()))
                            .findFirst()
                            .ifPresent(item -> {
                                item.setParentId(rootNode.getId());
                                item.setLevel(orphanNode.getLevel());
                                item.setPath(newPath);
                            });
                }
            }
        }

        // 5. 转换为VO
        List<CalcBaseRateDirectoryRespVO> allVOs = allItems.stream()
                .map(this::convertToRespVO)
                .collect(Collectors.toList());

        // 6. 构建树形结构
        return buildTree(allVOs, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSort(CalcBaseRateDirectorySwapSortReqVO swapReqVO) {
        // 1. 查询两个节点
        CalcBaseRateDirectoryDO node1 = calcBaseRateDirectoryMapper.selectById(swapReqVO.getNodeId1());
        CalcBaseRateDirectoryDO node2 = calcBaseRateDirectoryMapper.selectById(swapReqVO.getNodeId2());

        if (node1 == null || node2 == null) {
            throw exception(CALC_BASE_RATE_DIRECTORY_NOT_EXISTS);
        }

        // 2. 校验是否同级
        if (!isSameLevel(node1, node2)) {
            throw exception(CALC_BASE_RATE_DIRECTORY_NOT_SAME_LEVEL);
        }

        // 3. 交换排序值
        Integer tempSort = node1.getSortOrder();
        node1.setSortOrder(node2.getSortOrder());
        node2.setSortOrder(tempSort);

        // 4. 更新数据库
        calcBaseRateDirectoryMapper.updateById(node1);
        calcBaseRateDirectoryMapper.updateById(node2);

        log.info("[swapSort] 交换目录节点[{}]和节点[{}]的排序成功", swapReqVO.getNodeId1(), swapReqVO.getNodeId2());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void forceDeleteCalcBaseRateDirectory(Long id) {
        // 1. 校验节点存在
        validateCalcBaseRateDirectoryExists(id);

        // 2. 递归获取所有子孙节点ID
        List<Long> allDescendantIds = new ArrayList<>();
        allDescendantIds.add(id);
        collectDescendantIds(id, allDescendantIds);

        // 3. 从叶子节点开始删除（逆序）
        for (int i = allDescendantIds.size() - 1; i >= 0; i--) {
            Long nodeId = allDescendantIds.get(i);

            // 3.1 删除该节点关联的费率项
            List<CalcBaseRateItemDO> items = calcBaseRateItemMapper.selectListByCalcBaseRateDirectoryId(nodeId);
            for (CalcBaseRateItemDO item : items) {
                calcBaseRateItemMapper.deleteById(item.getId());
            }

            // 3.2 删除该节点
            calcBaseRateDirectoryMapper.deleteById(nodeId);
        }
    }

    // ==================== 私有方法 ====================

    private CalcBaseRateDirectoryDO validateCalcBaseRateDirectoryExists(Long id) {
        CalcBaseRateDirectoryDO item = calcBaseRateDirectoryMapper.selectById(id);
        if (item == null) {
            throw exception(CALC_BASE_RATE_DIRECTORY_NOT_EXISTS);
        }
        return item;
    }

    private CalcBaseRateDirectoryDO buildCalcBaseRateDirectoryDO(CalcBaseRateDirectorySaveReqVO reqVO) {
        return CalcBaseRateDirectoryDO.builder()
                .id(reqVO.getId())
                .calcBaseRateCatalogId(reqVO.getCalcBaseRateCatalogId())
                .parentId(reqVO.getParentId())
                .name(reqVO.getName())
                .sortOrder(reqVO.getSortOrder())
                .attributes(reqVO.getAttributes())
                .build();
    }

    private Integer getNextSortOrder(Long parentId, Long calcBaseRateCatalogId) {
        List<CalcBaseRateDirectoryDO> siblings = calcBaseRateDirectoryMapper.selectSiblings(parentId, calcBaseRateCatalogId);
        if (CollUtil.isEmpty(siblings)) {
            return 1;
        }
        return siblings.stream()
                .mapToInt(CalcBaseRateDirectoryDO::getSortOrder)
                .max()
                .orElse(0) + 1;
    }

    private CalcBaseRateDirectoryRespVO convertToRespVO(CalcBaseRateDirectoryDO item) {
        CalcBaseRateDirectoryRespVO vo = new CalcBaseRateDirectoryRespVO();
        vo.setId(item.getId());
        vo.setCalcBaseRateCatalogId(item.getCalcBaseRateCatalogId());
        vo.setParentId(item.getParentId());
        vo.setName(item.getName());
        vo.setSortOrder(item.getSortOrder());
        vo.setPath(item.getPath());
        vo.setLevel(item.getLevel());
        vo.setAttributes(item.getAttributes());
        vo.setCreateTime(item.getCreateTime());
        vo.setUpdateTime(item.getUpdateTime());
        return vo;
    }

    private List<CalcBaseRateDirectoryRespVO> buildTree(List<CalcBaseRateDirectoryRespVO> allNodes, Long parentId) {
        List<CalcBaseRateDirectoryRespVO> result = new ArrayList<>();
        for (CalcBaseRateDirectoryRespVO node : allNodes) {
            if ((parentId == null && node.getParentId() == null)
                    || (parentId != null && parentId.equals(node.getParentId()))) {
                node.setChildren(buildTree(allNodes, node.getId()));
                result.add(node);
            }
        }
        return result;
    }

    private boolean isSameLevel(CalcBaseRateDirectoryDO node1, CalcBaseRateDirectoryDO node2) {
        if (!node1.getCalcBaseRateCatalogId().equals(node2.getCalcBaseRateCatalogId())) {
            return false;
        }
        if (node1.getParentId() == null && node2.getParentId() == null) {
            return true;
        }
        if (node1.getParentId() == null || node2.getParentId() == null) {
            return false;
        }
        return node1.getParentId().equals(node2.getParentId());
    }

    private void collectDescendantIds(Long parentId, List<Long> ids) {
        List<CalcBaseRateDirectoryDO> children = calcBaseRateDirectoryMapper.selectListByParentId(parentId);
        for (CalcBaseRateDirectoryDO child : children) {
            ids.add(child.getId());
            collectDescendantIds(child.getId(), ids);
        }
    }

    @Override
    public List<com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO> getCalcBaseRateDirectoryTreeForWorkbench(Long calcBaseRateCatalogId) {
        // 1. 查询所有节点
        List<CalcBaseRateDirectoryDO> allItems = calcBaseRateDirectoryMapper.selectListByCalcBaseRateCatalogId(calcBaseRateCatalogId);

        // 2. 转换为工作台VO
        List<com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO> allVOs = allItems.stream()
                .map(this::convertToWorkbenchRespVO)
                .collect(Collectors.toList());

        // 3. 构建树形结构
        return buildWorkbenchTree(allVOs, null);
    }

    private com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO convertToWorkbenchRespVO(CalcBaseRateDirectoryDO item) {
        com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO vo = new com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO();
        vo.setId(item.getId());
        vo.setParentId(item.getParentId());
        vo.setName(item.getName());
        vo.setSortOrder(item.getSortOrder());
        return vo;
    }

    private List<com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO> buildWorkbenchTree(List<com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO> allNodes, Long parentId) {
        List<com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO> result = new ArrayList<>();
        for (com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateDirectoryRespVO node : allNodes) {
            if ((parentId == null && node.getParentId() == null)
                    || (parentId != null && parentId.equals(node.getParentId()))) {
                node.setChildren(buildWorkbenchTree(allNodes, node.getId()));
                result.add(node);
            }
        }
        return result;
    }
}
