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

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

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.yhy.module.core.controller.admin.boq.vo.BoqGuideTreeRespVO;
import com.yhy.module.core.controller.admin.boq.vo.BoqGuideTreeSaveReqVO;
import com.yhy.module.core.dal.dataobject.boq.BoqCatalogItemDO;
import com.yhy.module.core.dal.dataobject.boq.BoqGuideTreeDO;
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.dataobject.quota.QuotaItemDO;
import com.yhy.module.core.dal.mysql.boq.BoqCatalogItemMapper;
import com.yhy.module.core.dal.mysql.boq.BoqGuideTreeMapper;
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.dal.mysql.quota.QuotaCatalogTreeMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaItemMapper;
import com.yhy.module.core.service.boq.BoqGuideTreeService;
import java.util.ArrayList;
import java.util.Arrays;
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 BoqGuideTreeServiceImpl implements BoqGuideTreeService {

    @Resource
    private BoqGuideTreeMapper boqGuideTreeMapper;

    @Resource
    private BoqSubItemMapper boqSubItemMapper;

    @Resource
    private BoqItemTreeMapper boqItemTreeMapper;

    @Resource
    private BoqCatalogItemMapper boqCatalogItemMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private QuotaCatalogTreeMapper quotaCatalogTreeMapper;

    @Resource
    private QuotaItemMapper quotaItemMapper;

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

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

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

        // 4. 如果是quota类型且有定额ID，验证定额是否在范围内
        if ("quota".equals(createReqVO.getNodeType()) && createReqVO.getQuotaCatalogItemId() != null) {
            validateQuotaInRange(createReqVO.getBoqSubItemId(), createReqVO.getQuotaCatalogItemId());
        }

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

        // 6. 计算路径和层级
        if (createReqVO.getParentId() != null) {
            BoqGuideTreeDO parent = validateNodeExists(createReqVO.getParentId());
            // 校验父节点是否为目录类型
            if (!"directory".equals(parent.getNodeType())) {
                throw exception(BOQ_GUIDE_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<BoqGuideTreeDO> siblings = boqGuideTreeMapper.selectListByParentId(createReqVO.getParentId());
            node.setSortOrder(siblings.isEmpty() ? 1 : siblings.get(siblings.size() - 1).getSortOrder() + 1);
        }

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

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

        return node.getId();
    }

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

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

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

        // 4. 如果是quota类型且有定额ID，验证定额是否在范围内
        if ("quota".equals(updateReqVO.getNodeType()) && updateReqVO.getQuotaCatalogItemId() != null) {
            validateQuotaInRange(node.getBoqSubItemId(), updateReqVO.getQuotaCatalogItemId());
        }

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

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

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

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

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

        // 如果是quota类型，填充定额信息
        if ("quota".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<BoqGuideTreeRespVO> getTree(Long boqSubItemId) {
        // 查询所有节点
        List<BoqGuideTreeDO> allNodes = boqGuideTreeMapper.selectListByBoqSubItemId(boqSubItemId);

        // 转换为VO并填充定额信息
        List<BoqGuideTreeRespVO> allVOs = allNodes.stream()
                .map(node -> {
                    BoqGuideTreeRespVO vo = BeanUtils.toBean(node, BoqGuideTreeRespVO.class);
                    // 如果是quota类型，填充定额信息和价格
                    if ("quota".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());
                        }
                        // 查询定额基价获取价格信息
                        List<QuotaItemDO> quotaItems = quotaItemMapper.selectListByCatalogItemId(node.getQuotaCatalogItemId());
                        if (quotaItems != null && !quotaItems.isEmpty()) {
                            // 取第一条定额基价的价格（通常一个定额目录节点只对应一条定额基价）
                            QuotaItemDO quotaItem = quotaItems.get(0);
                            vo.setBasePriceExTax(quotaItem.getTaxExclBasePrice());
                            vo.setBasePriceInTax(quotaItem.getTaxInclBasePrice());
                        }
                    }
                    return vo;
                })
                .collect(Collectors.toList());

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

    @Override
    public List<BoqGuideTreeRespVO> getList(Long boqSubItemId) {
        List<BoqGuideTreeDO> nodes = boqGuideTreeMapper.selectListByBoqSubItemId(boqSubItemId);
        return nodes.stream()
                .map(node -> {
                    BoqGuideTreeRespVO vo = BeanUtils.toBean(node, BoqGuideTreeRespVO.class);
                    // 如果是quota类型，填充定额信息
                    if ("quota".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. 查询两个节点（加锁）
        BoqGuideTreeDO node1 = boqGuideTreeMapper.selectByIdForUpdate(nodeId1);
        BoqGuideTreeDO node2 = boqGuideTreeMapper.selectByIdForUpdate(nodeId2);

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

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

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

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

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

    /**
     * 校验节点存在
     */
    private BoqGuideTreeDO validateNodeExists(Long id) {
        BoqGuideTreeDO node = boqGuideTreeMapper.selectById(id);
        if (node == null) {
            throw exception(BOQ_GUIDE_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) {
        BoqGuideTreeDO existing = boqGuideTreeMapper.selectByBoqSubItemIdAndCode(boqSubItemId, code);
        if (existing != null && !existing.getId().equals(id)) {
            throw exception(BOQ_GUIDE_TREE_CODE_DUPLICATE, code);
        }
    }

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

        // quota类型必须有父节点（定额必须在目录下）
        if ("quota".equals(reqVO.getNodeType()) && reqVO.getParentId() == null) {
            throw exception(BOQ_GUIDE_TREE_QUOTA_NEED_PARENT);
        }

        // quota类型允许quotaCatalogItemId为空（用户可以只填写文字数据）
        // 不再强制要求关联定额

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

    /**
     * 验证定额是否在绑定的定额专业范围内
     * @param boqSubItemId 清单子项ID
     * @param quotaItemId 定额基价ID（yhy_quota_item表的ID）
     */
    private void validateQuotaInRange(Long boqSubItemId, Long quotaItemId) {
        // 1. 获取清单专业绑定的定额专业节点ID
        Long boundQuotaId = getQuotaCatalogItemIdByBoqSubItem(boqSubItemId);

        // 2. 查询定额基价
        QuotaItemDO quotaItem = quotaItemMapper.selectById(quotaItemId);
        if (quotaItem == null) {
            throw exception(BOQ_GUIDE_TREE_QUOTA_NOT_IN_RANGE);
        }

        // 3. 获取定额基价关联的定额子目树节点ID
        Long catalogTreeId = quotaItem.getCatalogItemId();
        if (catalogTreeId == null) {
            throw exception(BOQ_GUIDE_TREE_QUOTA_NOT_IN_RANGE);
        }

        // 4. 查询定额子目树节点，获取其关联的定额专业节点ID
        com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTree = 
            quotaCatalogTreeMapper.selectById(catalogTreeId);
        if (catalogTree == null) {
            throw exception(BOQ_GUIDE_TREE_QUOTA_NOT_IN_RANGE);
        }

        // 5. 获取定额专业节点ID
        Long quotaCatalogItemId = catalogTree.getCatalogItemId();
        if (quotaCatalogItemId == null) {
            throw exception(BOQ_GUIDE_TREE_QUOTA_NOT_IN_RANGE);
        }

        // 6. 直接比较定额专业节点ID是否匹配绑定的定额专业
        if (!quotaCatalogItemId.equals(boundQuotaId)) {
            throw exception(BOQ_GUIDE_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(BoqGuideTreeDO node1, BoqGuideTreeDO 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<BoqGuideTreeRespVO> buildTree(List<BoqGuideTreeRespVO> allNodes, Long parentId) {
        return allNodes.stream()
                .filter(node -> {
                    if (parentId == null) {
                        return node.getParentId() == null;
                    }
                    return parentId.equals(node.getParentId());
                })
                .peek(node -> {
                    List<BoqGuideTreeRespVO> children = buildTree(allNodes, node.getId());
                    if (!children.isEmpty()) {
                        node.setChildren(children);
                    }
                })
                .collect(Collectors.toList());
    }

    @Override
    public Long validateQuotaCodeInRange(Long boqSubItemId, String code) {
        if (boqSubItemId == null || code == null || code.trim().isEmpty()) {
            return null;
        }

        // 1. 获取清单专业绑定的定额基价第一层节点ID
        Long boundQuotaId = getQuotaCatalogItemIdByBoqSubItem(boqSubItemId);

        // 2. 根据编码查询定额基价
        List<QuotaItemDO> quotaItems = quotaItemMapper.selectByCode(code.trim());
        if (quotaItems == null || quotaItems.isEmpty()) {
            return null;
        }

        // 3. 遍历查询结果，找到在绑定范围内的定额
        for (QuotaItemDO quotaItem : quotaItems) {
            // 获取定额基价关联的定额子目树节点
            Long catalogItemId = quotaItem.getCatalogItemId();
            if (catalogItemId == null) {
                continue;
            }

            // 从定额子目树节点向上追溯到第一层（定额专业节点）
            // 定额子目树（yhy_quota_catalog_tree）通过 catalog_item_id 关联到定额专业树（yhy_quota_catalog_item）
            // 需要先查询定额子目树节点，获取其关联的定额专业节点ID
            com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTree = 
                quotaCatalogTreeMapper.selectById(catalogItemId);
            if (catalogTree == null) {
                continue;
            }

            // 获取定额专业节点ID（定额子目树直接关联到定额专业节点）
            Long quotaCatalogItemId = catalogTree.getCatalogItemId();
            if (quotaCatalogItemId == null) {
                continue;
            }

            // 直接比较定额专业节点ID是否匹配绑定的定额专业
            // boundQuotaId 是清单专业绑定的定额专业节点ID
            // quotaCatalogItemId 是定额基价所属的定额专业节点ID
            if (quotaCatalogItemId.equals(boundQuotaId)) {
                return quotaItem.getId();
            }
        }

        return null;
    }

    @Override
    public com.yhy.module.core.controller.admin.boq.vo.QuotaCodeValidateRespVO validateQuotaCodeWithInfo(Long boqSubItemId, String code) {
        if (boqSubItemId == null || code == null || code.trim().isEmpty()) {
            return null;
        }

        // 1. 获取清单专业绑定的定额专业节点ID
        Long boundQuotaId = getQuotaCatalogItemIdByBoqSubItem(boqSubItemId);

        // 2. 根据编码查询定额基价
        List<QuotaItemDO> quotaItems = quotaItemMapper.selectByCode(code.trim());
        if (quotaItems == null || quotaItems.isEmpty()) {
            return null;
        }

        // 3. 遍历查询结果，找到在绑定范围内的定额
        for (QuotaItemDO quotaItem : quotaItems) {
            Long catalogItemId = quotaItem.getCatalogItemId();
            if (catalogItemId == null) {
                continue;
            }

            // 查询定额子目树节点
            com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTree = 
                quotaCatalogTreeMapper.selectById(catalogItemId);
            if (catalogTree == null) {
                continue;
            }

            // 获取定额专业节点ID
            Long quotaCatalogItemId = catalogTree.getCatalogItemId();
            if (quotaCatalogItemId == null) {
                continue;
            }

            // 比较定额专业节点ID
            if (quotaCatalogItemId.equals(boundQuotaId)) {
                // 返回定额信息
                return com.yhy.module.core.controller.admin.boq.vo.QuotaCodeValidateRespVO.builder()
                        .id(quotaItem.getId())
                        .code(quotaItem.getCode())
                        .name(quotaItem.getName())
                        .unit(quotaItem.getUnit())
                        .build();
            }
        }

        return null;
    }
}
