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

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemRespVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemSaveReqVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryFullRespVO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeMappingDO;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCategoryMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMappingMapper;
import com.yhy.module.core.enums.ErrorCodeConstants;
import com.yhy.module.core.service.quota.QuotaCatalogItemService;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
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 QuotaCatalogItemServiceImpl implements QuotaCatalogItemService {

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private ResourceCategoryTreeMapper resourceCategoryTreeMapper;

    @Resource
    private ResourceCategoryMapper resourceCategoryMapper;

    @Resource
    private ResourceCategoryTreeMappingMapper resourceCategoryTreeMappingMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createQuotaCatalogItem(QuotaCatalogItemSaveReqVO createReqVO) {
        // 转换�?DO
        QuotaCatalogItemDO catalogItem = convertToDO(createReqVO);
        
        // 设置路径
        if (createReqVO.getParentId() != null) {
            QuotaCatalogItemDO parent = validateExists(createReqVO.getParentId());
            String[] parentPath = parent.getPath();
            String[] newPath = new String[parentPath.length + 1];
            System.arraycopy(parentPath, 0, newPath, 0, parentPath.length);
            newPath[parentPath.length] = parent.getId().toString();
            catalogItem.setPath(newPath);
        } else {
            catalogItem.setPath(new String[0]);
        }
        
        // 插入
        quotaCatalogItemMapper.insert(catalogItem);
        return catalogItem.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateQuotaCatalogItem(QuotaCatalogItemSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());
        
        // 转换�?DO
        QuotaCatalogItemDO updateObj = convertToDO(updateReqVO);
        updateObj.setId(updateReqVO.getId());
        
        // 更新
        quotaCatalogItemMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteQuotaCatalogItem(Long id) {
        // 校验存在
        validateExists(id);
        
        // 检查是否有子节�?
        if (quotaCatalogItemMapper.hasChildren(id)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_HAS_CHILDREN);
        }
        
        // 删除
        quotaCatalogItemMapper.deleteById(id);
    }

    @Override
    public QuotaCatalogItemDO getQuotaCatalogItem(Long id) {
        return quotaCatalogItemMapper.selectById(id);
    }

    @Override
    public List<QuotaCatalogItemRespVO> getQuotaCatalogItemList() {
        // 只查询第一层（定额专业节点）
        List<QuotaCatalogItemDO> list = quotaCatalogItemMapper.selectRootList();
        return list.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindResourceSpecialty(Long catalogItemId, Long categoryTreeId) {
        // 1. 校验定额专业节点
        QuotaCatalogItemDO catalogItem = validateExists(catalogItemId);
        
        // 验证节点类型
        if (!"specialty".equals(catalogItem.getNodeType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_SPECIALTY);
        }
        
        // 验证是否已绑�?
        if (Boolean.TRUE.equals(catalogItem.isSpecialtyLocked())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_ALREADY_BOUND);
        }
        
        // 2. 校验工料机专业节�?
        ResourceCategoryTreeDO categoryTree = resourceCategoryTreeMapper.selectById(categoryTreeId);
        if (categoryTree == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_EXISTS);
        }
        
        if (!"specialty".equals(categoryTree.getNodeType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_SPECIALTY);
        }
        
        // 3. 执行绑定
        catalogItem.setCategoryTreeId(categoryTreeId);
        catalogItem.setSpecialtyLocked(true);
        if (catalogItem.getAttributes() == null) {
            catalogItem.setAttributes(new HashMap<>());
        }
        catalogItem.getAttributes().put("bind_time", LocalDateTime.now().toString());
        
        quotaCatalogItemMapper.updateById(catalogItem);
        
        log.info("[bindResourceSpecialty] 绑定成功，catalogItemId={}, categoryTreeId={}", catalogItemId, categoryTreeId);
    }

    @Override
    public void validateNodeType(Long id, String expectedType) {
        QuotaCatalogItemDO catalogItem = validateExists(id);
        String actualType = catalogItem.getNodeType();
        if (!expectedType.equals(actualType)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_TYPE_MISMATCH);
        }
    }

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

    private QuotaCatalogItemDO validateExists(Long id) {
        QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(id);
        if (catalogItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }
        return catalogItem;
    }

    private QuotaCatalogItemDO convertToDO(QuotaCatalogItemSaveReqVO vo) {
        QuotaCatalogItemDO catalogItem = new QuotaCatalogItemDO();
        catalogItem.setParentId(vo.getParentId());
        catalogItem.setCategoryTreeId(vo.getCategoryTreeId());
        catalogItem.setCode(vo.getCode());
        catalogItem.setName(vo.getName());
        catalogItem.setUnit(vo.getUnit());
        catalogItem.setSortOrder(vo.getSortOrder());
        catalogItem.setAttributes(vo.getAttributes());
        String nodeType = resolveNodeType(vo, vo.getId() == null);
        if (StrUtil.isNotBlank(nodeType)) {
            catalogItem.setNodeType(nodeType);
        }
        return catalogItem;
    }

    private QuotaCatalogItemRespVO convertToVO(QuotaCatalogItemDO catalogItem) {
        QuotaCatalogItemRespVO vo = new QuotaCatalogItemRespVO();
        vo.setId(catalogItem.getId());
        vo.setParentId(catalogItem.getParentId());
        vo.setCategoryTreeId(catalogItem.getCategoryTreeId());
        vo.setCode(catalogItem.getCode());
        vo.setName(catalogItem.getName());
        vo.setUnit(catalogItem.getUnit());
        vo.setPath(catalogItem.getPath());
        vo.setSortOrder(catalogItem.getSortOrder());
        vo.setAttributes(catalogItem.getAttributes());
        vo.setCreateTime(catalogItem.getCreateTime());
        vo.setUpdateTime(catalogItem.getUpdateTime());
        
        // 扩展字段
        vo.setNodeType(catalogItem.getNodeType());
        vo.setSpecialtyLocked(catalogItem.isSpecialtyLocked());
        vo.setContentType(catalogItem.getContentType());
        vo.setAllowChildren(catalogItem.isAllowChildren());
        
        return vo;
    }

    private String resolveNodeType(QuotaCatalogItemSaveReqVO vo, boolean required) {
        String nodeType = vo.getNodeType();
        if (StrUtil.isBlank(nodeType) && vo.getAttributes() != null) {
            Object attrNodeType = vo.getAttributes().get("node_type");
            if (attrNodeType != null) {
                nodeType = String.valueOf(attrNodeType);
            }
        }
        if (required && StrUtil.isBlank(nodeType)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_TYPE_MISMATCH);
        }
        return nodeType;
    }

    private ResourceCategoryFullRespVO convertToCategoryFullVO(ResourceCategoryDO category) {
        ResourceCategoryFullRespVO vo = new ResourceCategoryFullRespVO();
        vo.setId(category.getId());
        vo.setCode(category.getCode());
        vo.setName(category.getName());
        vo.setTaxExclBaseCode(category.getTaxExclBaseCode());
        vo.setTaxInclBaseCode(category.getTaxInclBaseCode());
        vo.setTaxExclCompileCode(category.getTaxExclCompileCode());
        vo.setTaxInclCompileCode(category.getTaxInclCompileCode());
        vo.setSortOrder(category.getSortOrder());
        return vo;
    }

    @Override
    public List<QuotaCatalogItemRespVO> getQuotaCatalogItemTree(List<String> excludeNodeTypes) {
        // 查询所有节点
        List<QuotaCatalogItemDO> allNodes = quotaCatalogItemMapper.selectList();
        
        // 如果有排除类型，先过滤
        if (excludeNodeTypes != null && !excludeNodeTypes.isEmpty()) {
            Set<String> excludeSet = excludeNodeTypes.stream()
                    .filter(StrUtil::isNotBlank)
                    .collect(Collectors.toSet());
            if (!excludeSet.isEmpty()) {
                allNodes = allNodes.stream()
                        .filter(node -> !excludeSet.contains(node.getNodeType()))
                        .collect(Collectors.toList());
            }
        }
        
        // 转换为VO
        List<QuotaCatalogItemRespVO> allVOs = allNodes.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
        
        // 构建树形结构
        return buildTree(allVOs, null);
    }

    /**
     * 构建树形结构
     *
     * @param nodes 所有节点
     * @param parentId 父节点ID
     * @return 树形结构
     */
    private List<QuotaCatalogItemRespVO> buildTree(List<QuotaCatalogItemRespVO> nodes, Long parentId) {
        return nodes.stream()
                .filter(node -> Objects.equals(node.getParentId(), parentId))
                .sorted(Comparator.comparing(QuotaCatalogItemRespVO::getSortOrder))
                .peek(node -> {
                    List<QuotaCatalogItemRespVO> children = buildTree(nodes, node.getId());
                    node.setChildren(children);
                })
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSort(Long nodeId1, Long nodeId2) {
        // 使用行锁查询两个节点
        QuotaCatalogItemDO node1 = quotaCatalogItemMapper.selectByIdForUpdate(nodeId1);
        QuotaCatalogItemDO node2 = quotaCatalogItemMapper.selectByIdForUpdate(nodeId2);
        
        // 验证节点存在
        if (node1 == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }
        if (node2 == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }
        
        // 验证是否同级
        if (!Objects.equals(node1.getParentId(), node2.getParentId())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_SAME_LEVEL);
        }
        
        // 交换 sort_order
        Integer tempSort = node1.getSortOrder();
        node1.setSortOrder(node2.getSortOrder());
        node2.setSortOrder(tempSort);
        
        // 更新数据库
        quotaCatalogItemMapper.updateById(node1);
        quotaCatalogItemMapper.updateById(node2);
    }

    @Override
    public List<ResourceCategoryFullRespVO> getCategoriesByCatalogItem(Long catalogItemId) {
        // 1. 查询当前节点
        QuotaCatalogItemDO currentNode = validateExists(catalogItemId);
        
        // 2. 如果是费率模式节点，向上查找定额专业节点
        QuotaCatalogItemDO specialtyNode = currentNode;
        if ("rate_mode".equals(currentNode.getNodeType())) {
            if (currentNode.getParentId() == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
            }
            specialtyNode = validateExists(currentNode.getParentId());
        }
        
        // 3. 验证是定额专业节点
        if (!"specialty".equals(specialtyNode.getNodeType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_SPECIALTY);
        }
        
        // 4. 获取绑定的工料机专业ID
        Long categoryTreeId = specialtyNode.getCategoryTreeId();
        if (categoryTreeId == null) {
            log.warn("[getCategoriesByCatalogItem] 定额专业节点未绑定工料机专业，catalogItemId={}", catalogItemId);
            return new ArrayList<>();
        }
        
        // 5. 查询工料机专业下的类别映射关系
        List<ResourceCategoryTreeMappingDO> mappings = resourceCategoryTreeMappingMapper.selectList(
                new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                        .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, categoryTreeId)
        );
        
        if (mappings.isEmpty()) {
            log.warn("[getCategoriesByCatalogItem] 工料机专业下没有关联的类别，categoryTreeId={}", categoryTreeId);
            return new ArrayList<>();
        }
        
        // 6. 提取类别ID列表
        List<Long> categoryIds = mappings.stream()
                .map(ResourceCategoryTreeMappingDO::getCategoryId)
                .collect(Collectors.toList());
        
        // 7. 查询类别字典详情（含价格代码）
        List<ResourceCategoryDO> categories = resourceCategoryMapper.selectList(
                new LambdaQueryWrapper<ResourceCategoryDO>()
                        .in(ResourceCategoryDO::getId, categoryIds)
                        .orderByAsc(ResourceCategoryDO::getSortOrder)
        );
        
        // 8. 转换为VO
        return categories.stream()
                .map(this::convertToCategoryFullVO)
                .collect(Collectors.toList());
    }

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