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

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.yhy.module.core.controller.admin.boq.vo.BoqDetailTreeRespVO;
import com.yhy.module.core.controller.admin.boq.vo.BoqDetailTreeSaveReqVO;
import com.yhy.module.core.dal.dataobject.boq.BoqCatalogItemDO;
import com.yhy.module.core.dal.dataobject.boq.BoqDetailTreeDO;
import com.yhy.module.core.dal.dataobject.boq.BoqItemTreeDO;
import com.yhy.module.core.dal.dataobject.boq.BoqSubItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO;
import com.yhy.module.core.dal.mysql.boq.BoqCatalogItemMapper;
import com.yhy.module.core.dal.mysql.boq.BoqDetailTreeMapper;
import com.yhy.module.core.dal.mysql.boq.BoqItemTreeMapper;
import com.yhy.module.core.dal.mysql.boq.BoqSubItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper;
import com.yhy.module.core.service.boq.BoqDetailTreeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yhy.module.core.enums.ErrorCodeConstants.*;

/**
 * 清单明细树 Service 实现类
 *
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class BoqDetailTreeServiceImpl implements BoqDetailTreeService {

    @Resource
    private BoqDetailTreeMapper boqDetailTreeMapper;

    @Resource
    private BoqSubItemMapper boqSubItemMapper;

    @Resource
    private BoqItemTreeMapper boqItemTreeMapper;

    @Resource
    private BoqCatalogItemMapper boqCatalogItemMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createNode(BoqDetailTreeSaveReqVO createReqVO) {
        // 1. 校验清单子项存在
        validateBoqSubItemExists(createReqVO.getBoqSubItemId());

        // 2. 校验编码唯一性
        validateCodeUnique(null, createReqVO.getBoqSubItemId(), createReqVO.getCode());

        // 3. 校验节点类型
        validateNodeType(createReqVO);

        // 4. 如果是content类型，验证定额是否在范围内
        if ("content".equals(createReqVO.getNodeType())) {
            validateQuotaInRange(createReqVO.getBoqSubItemId(), createReqVO.getQuotaCatalogItemId());
        }

        // 5. 构建节点
        BoqDetailTreeDO node = BeanUtils.toBean(createReqVO, BoqDetailTreeDO.class);

        // 6. 计算路径和层级
        if (createReqVO.getParentId() != null) {
            BoqDetailTreeDO parent = validateNodeExists(createReqVO.getParentId());
            // 校验父节点是否为目录类型
            if (!"directory".equals(parent.getNodeType())) {
                throw exception(BOQ_DETAIL_TREE_PARENT_NOT_DIRECTORY);
            }
            // 设置路径和层级
            List<String> pathList = new ArrayList<>(Arrays.asList(parent.getPath()));
            pathList.add(String.valueOf(node.getId()));
            node.setPath(pathList.toArray(new String[0]));
            node.setLevel(parent.getLevel() + 1);
        } else {
            node.setPath(new String[]{String.valueOf(node.getId())});
            node.setLevel(1);
        }

        // 7. 设置排序
        if (node.getSortOrder() == null) {
            List<BoqDetailTreeDO> siblings = boqDetailTreeMapper.selectListByParentId(createReqVO.getParentId());
            node.setSortOrder(siblings.isEmpty() ? 1 : siblings.get(siblings.size() - 1).getSortOrder() + 1);
        }

        // 8. 插入节点
        boqDetailTreeMapper.insert(node);

        // 9. 更新路径（因为ID是插入后才生成的）
        List<String> pathList = new ArrayList<>();
        if (createReqVO.getParentId() != null) {
            BoqDetailTreeDO parent = boqDetailTreeMapper.selectById(createReqVO.getParentId());
            pathList.addAll(Arrays.asList(parent.getPath()));
        }
        pathList.add(String.valueOf(node.getId()));
        node.setPath(pathList.toArray(new String[0]));
        boqDetailTreeMapper.updateById(node);

        return node.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateNode(BoqDetailTreeSaveReqVO updateReqVO) {
        // 1. 校验节点存在
        BoqDetailTreeDO node = validateNodeExists(updateReqVO.getId());

        // 2. 校验编码唯一性
        validateCodeUnique(updateReqVO.getId(), node.getBoqSubItemId(), updateReqVO.getCode());

        // 3. 校验节点类型
        validateNodeType(updateReqVO);

        // 4. 如果是content类型，验证定额是否在范围内
        if ("content".equals(updateReqVO.getNodeType())) {
            validateQuotaInRange(node.getBoqSubItemId(), updateReqVO.getQuotaCatalogItemId());
        }

        // 5. 更新节点
        BoqDetailTreeDO updateObj = BeanUtils.toBean(updateReqVO, BoqDetailTreeDO.class);
        boqDetailTreeMapper.updateById(updateObj);
    }

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

        // 2. 校验是否有子节点
        if (boqDetailTreeMapper.hasChildren(id)) {
            throw exception(BOQ_DETAIL_TREE_HAS_CHILDREN);
        }

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

    @Override
    public BoqDetailTreeRespVO getNode(Long id) {
        BoqDetailTreeDO node = validateNodeExists(id);
        BoqDetailTreeRespVO vo = BeanUtils.toBean(node, BoqDetailTreeRespVO.class);

        // 如果是content类型，填充定额信息
        if ("content".equals(node.getNodeType()) && node.getQuotaCatalogItemId() != null) {
            QuotaCatalogItemDO quota = quotaCatalogItemMapper.selectById(node.getQuotaCatalogItemId());
            if (quota != null) {
                vo.setQuotaCode(quota.getCode());
                vo.setQuotaName(quota.getName());
                vo.setQuotaUnit(quota.getUnit());
            }
        }

        return vo;
    }

    @Override
    public List<BoqDetailTreeRespVO> getTree(Long boqSubItemId) {
        // 查询所有节点
        List<BoqDetailTreeDO> allNodes = boqDetailTreeMapper.selectListByBoqSubItemId(boqSubItemId);

        // 转换为VO并填充定额信息
        List<BoqDetailTreeRespVO> allVOs = allNodes.stream()
                .map(node -> {
                    BoqDetailTreeRespVO vo = BeanUtils.toBean(node, BoqDetailTreeRespVO.class);
                    // 如果是content类型，填充定额信息
                    if ("content".equals(node.getNodeType()) && node.getQuotaCatalogItemId() != null) {
                        QuotaCatalogItemDO quota = quotaCatalogItemMapper.selectById(node.getQuotaCatalogItemId());
                        if (quota != null) {
                            vo.setQuotaCode(quota.getCode());
                            vo.setQuotaName(quota.getName());
                            vo.setQuotaUnit(quota.getUnit());
                        }
                    }
                    return vo;
                })
                .collect(Collectors.toList());

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

    @Override
    public List<BoqDetailTreeRespVO> getList(Long boqSubItemId) {
        List<BoqDetailTreeDO> nodes = boqDetailTreeMapper.selectListByBoqSubItemId(boqSubItemId);
        return nodes.stream()
                .map(node -> {
                    BoqDetailTreeRespVO vo = BeanUtils.toBean(node, BoqDetailTreeRespVO.class);
                    // 如果是content类型，填充定额信息
                    if ("content".equals(node.getNodeType()) && node.getQuotaCatalogItemId() != null) {
                        QuotaCatalogItemDO quota = quotaCatalogItemMapper.selectById(node.getQuotaCatalogItemId());
                        if (quota != null) {
                            vo.setQuotaCode(quota.getCode());
                            vo.setQuotaName(quota.getName());
                            vo.setQuotaUnit(quota.getUnit());
                        }
                    }
                    return vo;
                })
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSort(Long nodeId1, Long nodeId2) {
        // 1. 查询两个节点（加锁）
        BoqDetailTreeDO node1 = boqDetailTreeMapper.selectByIdForUpdate(nodeId1);
        BoqDetailTreeDO node2 = boqDetailTreeMapper.selectByIdForUpdate(nodeId2);

        // 2. 校验存在
        if (node1 == null || node2 == null) {
            throw exception(BOQ_DETAIL_TREE_NOT_EXISTS);
        }

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

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

        // 5. 更新数据库
        boqDetailTreeMapper.updateById(node1);
        boqDetailTreeMapper.updateById(node2);
    }

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

    /**
     * 校验节点存在
     */
    private BoqDetailTreeDO validateNodeExists(Long id) {
        BoqDetailTreeDO node = boqDetailTreeMapper.selectById(id);
        if (node == null) {
            throw exception(BOQ_DETAIL_TREE_NOT_EXISTS);
        }
        return node;
    }

    /**
     * 校验清单子项存在
     */
    private void validateBoqSubItemExists(Long boqSubItemId) {
        if (boqSubItemMapper.selectById(boqSubItemId) == null) {
            throw exception(BOQ_SUB_ITEM_NOT_EXISTS);
        }
    }

    /**
     * 校验编码唯一性
     */
    private void validateCodeUnique(Long id, Long boqSubItemId, String code) {
        BoqDetailTreeDO existing = boqDetailTreeMapper.selectByBoqSubItemIdAndCode(boqSubItemId, code);
        if (existing != null && !existing.getId().equals(id)) {
            throw exception(BOQ_DETAIL_TREE_CODE_DUPLICATE, code);
        }
    }

    /**
     * 校验节点类型
     */
    private void validateNodeType(BoqDetailTreeSaveReqVO reqVO) {
        if (!"directory".equals(reqVO.getNodeType()) && !"content".equals(reqVO.getNodeType())) {
            throw exception(BOQ_DETAIL_TREE_INVALID_NODE_TYPE);
        }

        // content类型必须有定额ID
        if ("content".equals(reqVO.getNodeType()) && reqVO.getQuotaCatalogItemId() == null) {
            throw exception(BOQ_DETAIL_TREE_CONTENT_NEED_QUOTA);
        }

        // directory类型不能有定额ID
        if ("directory".equals(reqVO.getNodeType()) && reqVO.getQuotaCatalogItemId() != null) {
            throw exception(BOQ_DETAIL_TREE_DIRECTORY_NO_QUOTA);
        }
    }

    /**
     * 验证定额是否在绑定的定额专业范围内
     */
    private void validateQuotaInRange(Long boqSubItemId, Long quotaCatalogItemId) {
        // 1. 获取清单专业绑定的定额基价第一层节点ID
        Long boundQuotaId = getQuotaCatalogItemIdByBoqSubItem(boqSubItemId);

        // 2. 查询定额节点
        QuotaCatalogItemDO quotaNode = quotaCatalogItemMapper.selectById(quotaCatalogItemId);
        if (quotaNode == null) {
            throw exception(BOQ_DETAIL_TREE_QUOTA_NOT_IN_RANGE);
        }

        // 3. 验证是否为内容节点
        if (!"content".equals(quotaNode.getContentType())) {
            throw exception(BOQ_DETAIL_TREE_QUOTA_NOT_CONTENT_TYPE);
        }

        // 4. 从定额节点向上追溯到第一层
        QuotaCatalogItemDO current = quotaNode;
        while (current != null && current.getParentId() != null) {
            current = quotaCatalogItemMapper.selectById(current.getParentId());
        }

        // 5. 验证是否匹配
        if (current == null || !current.getId().equals(boundQuotaId)) {
            throw exception(BOQ_DETAIL_TREE_QUOTA_NOT_IN_RANGE);
        }
    }

    /**
     * 从清单子项追溯到清单专业的绑定定额
     */
    private Long getQuotaCatalogItemIdByBoqSubItem(Long boqSubItemId) {
        // 1. 查询清单子项
        BoqSubItemDO subItem = boqSubItemMapper.selectById(boqSubItemId);
        if (subItem == null) {
            throw exception(BOQ_SUB_ITEM_NOT_EXISTS);
        }

        // 2. 查询清单项树
        BoqItemTreeDO itemTree = boqItemTreeMapper.selectById(subItem.getBoqItemTreeId());
        if (itemTree == null) {
            throw exception(BOQ_ITEM_TREE_NOT_EXISTS);
        }

        // 3. 查询清单专业
        BoqCatalogItemDO catalogItem = boqCatalogItemMapper.selectById(itemTree.getBoqCatalogItemId());
        if (catalogItem == null) {
            throw exception(BOQ_CATALOG_ITEM_NOT_EXISTS);
        }

        // 4. 返回绑定的定额基价第一层节点ID
        if (catalogItem.getQuotaCatalogItemId() == null) {
            throw exception(BOQ_CATALOG_ITEM_NOT_BOUND_QUOTA);
        }

        return catalogItem.getQuotaCatalogItemId();
    }

    /**
     * 判断两个节点是否同级
     */
    private boolean isSameLevel(BoqDetailTreeDO node1, BoqDetailTreeDO node2) {
        // 同一清单子项下
        if (!node1.getBoqSubItemId().equals(node2.getBoqSubItemId())) {
            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 List<BoqDetailTreeRespVO> buildTree(List<BoqDetailTreeRespVO> allNodes, Long parentId) {
        return allNodes.stream()
                .filter(node -> {
                    if (parentId == null) {
                        return node.getParentId() == null;
                    }
                    return parentId.equals(node.getParentId());
                })
                .peek(node -> {
                    List<BoqDetailTreeRespVO> children = buildTree(allNodes, node.getId());
                    if (!children.isEmpty()) {
                        node.setChildren(children);
                    }
                })
                .collect(Collectors.toList());
    }
}
