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

import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yhy.module.core.controller.admin.resource.vo.ResourceCategorySimpleRespVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryTreeMappingSaveReqVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryTreeNodeVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryTreeSaveReqVO;
import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceResourceDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO;
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.dataobject.resource.ResourceItemDO;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceResourceMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCatalogItemMapper;
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.dal.mysql.resource.ResourceItemMapper;
import com.yhy.module.core.service.resource.ResourceCategoryTreeService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 工料机机类树 Service 实现
 *
 * @author yihuiyong
 */
@Service
public class ResourceCategoryTreeServiceImpl implements ResourceCategoryTreeService {

    @Resource
    private ResourceCategoryTreeMapper categoryTreeMapper;

    @Resource
    private ResourceCategoryTreeMappingMapper mappingMapper;

    @Resource
    private ResourceCategoryMapper categoryMapper;

    @Resource
    private ResourceItemMapper resourceItemMapper;
    
    @Resource
    private ResourceCatalogItemMapper catalogItemMapper;
    
    @Resource
    private InfoPriceResourceMapper infoPriceResourceMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createNode(ResourceCategoryTreeSaveReqVO reqVO) {
        Long tenantId = TenantContextHolder.getTenantId();

        // 不检查编码唯一性，允许同一父节点下有相同编码
        // 编码由前端自动生成（如：父编码-随机数）

        ResourceCategoryTreeDO node = BeanUtils.toBean(reqVO, ResourceCategoryTreeDO.class);
        node.setTenantId(tenantId);

        // 构建路径
        if (reqVO.getParentId() != null) {
            ResourceCategoryTreeDO parent = categoryTreeMapper.selectById(reqVO.getParentId());
            if (parent == null) {
                throw exception0(BAD_REQUEST.getCode(), "父节点不存在");
            }
            List<String> pathList = new ArrayList<>(Arrays.asList(parent.getPath()));
            pathList.add(String.valueOf(node.getId()));
            node.setPath(pathList.toArray(new String[0]));
        } else {
            node.setPath(new String[]{String.valueOf(node.getId())});
        }

        categoryTreeMapper.insert(node);

        // 更新路径（因为插入后才有ID）
        if (reqVO.getParentId() != null) {
            ResourceCategoryTreeDO parent = categoryTreeMapper.selectById(reqVO.getParentId());
            List<String> pathList = new ArrayList<>(Arrays.asList(parent.getPath()));
            pathList.add(String.valueOf(node.getId()));
            node.setPath(pathList.toArray(new String[0]));
        } else {
            node.setPath(new String[]{String.valueOf(node.getId())});
        }
        categoryTreeMapper.updateById(node);

        return node.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateNode(ResourceCategoryTreeSaveReqVO reqVO) {
        ResourceCategoryTreeDO db = categoryTreeMapper.selectById(reqVO.getId());
        if (db == null) {
            throw exception0(BAD_REQUEST.getCode(), "节点不存在");
        }

        ResourceCategoryTreeDO node = BeanUtils.toBean(reqVO, ResourceCategoryTreeDO.class);
        node.setTenantId(TenantContextHolder.getTenantId());
        categoryTreeMapper.updateById(node);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteNode(Long id) {
        Long tenantId = TenantContextHolder.getTenantId();
        
        // 1. 查询节点信息
        ResourceCategoryTreeDO node = categoryTreeMapper.selectById(id);
        if (node == null) {
            throw exception0(BAD_REQUEST.getCode(), "节点不存在");
        }
        
        // 2. 检查第三层：工料机目录树（所有节点都需要检查）
        Long catalogItemCount = catalogItemMapper.selectCount(
                new LambdaQueryWrapper<ResourceCatalogItemDO>()
                        .eq(ResourceCatalogItemDO::getTenantId, tenantId)
                        .eq(ResourceCatalogItemDO::getCategoryTreeId, id)
                        .eq(ResourceCatalogItemDO::getDeleted, 0)
        );
        if (catalogItemCount > 0) {
            throw exception0(BAD_REQUEST.getCode(), 
                    String.format("该节点下存在 %d 个工料机目录树节点，无法删除", catalogItemCount));
        }
        
        // 3. 根据节点类型进行不同的检查
        if ("specialty".equals(node.getNodeType())) {
            // 工料机专业节点：检查是否有类别映射
            Long mappingCount = mappingMapper.selectCount(
                    new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                            .eq(ResourceCategoryTreeMappingDO::getTenantId, tenantId)
                            .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, id)
            );
            if (mappingCount > 0) {
                throw exception0(BAD_REQUEST.getCode(), 
                        String.format("该工料机专业下存在 %d 个类别关联，无法删除", mappingCount));
            }
        } else if ("region".equals(node.getNodeType())) {
            // 省市节点：检查是否有子节点
            Long childCount = categoryTreeMapper.selectCount(
                    new LambdaQueryWrapper<ResourceCategoryTreeDO>()
                            .eq(ResourceCategoryTreeDO::getParentId, id)
            );
            if (childCount > 0) {
                throw exception0(BAD_REQUEST.getCode(), 
                        String.format("该省市节点下存在 %d 个子节点，无法删除", childCount));
            }
        } else {
            // 其他类型节点：通用检查（检查子节点）
            Long childCount = categoryTreeMapper.selectCount(
                    new LambdaQueryWrapper<ResourceCategoryTreeDO>()
                            .eq(ResourceCategoryTreeDO::getParentId, id)
            );
            if (childCount > 0) {
                throw exception0(BAD_REQUEST.getCode(), 
                        String.format("该节点下存在 %d 个子节点，无法删除", childCount));
            }
        }
        
        // 4. 删除关联关系（如果有）
        mappingMapper.delete(new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, id));
        
        // 5. 删除节点
        categoryTreeMapper.deleteById(id);
    }

    @Override
    public List<ResourceCategoryTreeNodeVO> getTree() {
        Long tenantId = TenantContextHolder.getTenantId();
        List<ResourceCategoryTreeDO> allNodes = categoryTreeMapper.selectList(
                new LambdaQueryWrapper<ResourceCategoryTreeDO>()
                        .eq(ResourceCategoryTreeDO::getTenantId, tenantId)
                        .orderByAsc(ResourceCategoryTreeDO::getSortOrder)
        );

        return buildTree(allNodes, null);
    }

    @Override
    public List<ResourceCategorySimpleRespVO> getCategoriesByTreeNode(Long treeNodeId) {
        Long tenantId = TenantContextHolder.getTenantId();

        // 查询关联关系
        List<ResourceCategoryTreeMappingDO> mappings = mappingMapper.selectList(
                new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                        .eq(ResourceCategoryTreeMappingDO::getTenantId, tenantId)
                        .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, treeNodeId)
        );

        if (mappings.isEmpty()) {
            return new ArrayList<>();
        }

        List<Long> categoryIds = mappings.stream()
                .map(ResourceCategoryTreeMappingDO::getCategoryId)
                .collect(Collectors.toList());

        // 查询类别详情，直接按类别字典的 sort_order 排序
        List<ResourceCategoryDO> categories = categoryMapper.selectList(
                new LambdaQueryWrapper<ResourceCategoryDO>()
                        .in(ResourceCategoryDO::getId, categoryIds)
                        .orderByAsc(ResourceCategoryDO::getSortOrder)
        );

        // 转换为简化VO（使用类别字典的排序）
        return categories.stream()
                .map(category -> {
                    ResourceCategorySimpleRespVO vo = new ResourceCategorySimpleRespVO();
                    vo.setId(category.getId());
                    vo.setCode(category.getCode());
                    vo.setName(category.getName());
                    vo.setSortOrder(category.getSortOrder());
                    return vo;
                })
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addCategoryMapping(ResourceCategoryTreeMappingSaveReqVO reqVO) {
        Long tenantId = TenantContextHolder.getTenantId();

        // 1. 验证节点类型
        ResourceCategoryTreeDO treeNode = categoryTreeMapper.selectById(reqVO.getCategoryTreeId());
        if (treeNode == null) {
            throw exception0(BAD_REQUEST.getCode(), "机类树节点不存在");
        }
        if (!"specialty".equals(treeNode.getNodeType())) {
            throw exception0(BAD_REQUEST.getCode(), "只有工料机专业类型的节点才能添加类别列表");
        }

        // 2. 检查是否已存在
        ResourceCategoryTreeMappingDO exists = mappingMapper.selectOne(
                new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                        .eq(ResourceCategoryTreeMappingDO::getTenantId, tenantId)
                        .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, reqVO.getCategoryTreeId())
                        .eq(ResourceCategoryTreeMappingDO::getCategoryId, reqVO.getCategoryId())
        );
        if (exists != null) {
            throw exception0(BAD_REQUEST.getCode(), "关联关系已存在");
        }

        // 3. 添加关联
        ResourceCategoryTreeMappingDO mapping = BeanUtils.toBean(reqVO, ResourceCategoryTreeMappingDO.class);
        mapping.setTenantId(tenantId);
        mappingMapper.insert(mapping);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeCategoryMapping(Long treeNodeId, Long categoryId) {
        Long tenantId = TenantContextHolder.getTenantId();
        
        // 检查映射是否被引用
        checkMappingReference(tenantId, treeNodeId, categoryId);
        
        // 删除关联关系
        mappingMapper.delete(new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, treeNodeId)
                .eq(ResourceCategoryTreeMappingDO::getCategoryId, categoryId));
    }
    
    /**
     * 检查映射关系是否被引用
     * 
     * @param tenantId 租户ID
     * @param treeNodeId 机类树节点ID
     * @param categoryId 类别ID
     * @throws RuntimeException 如果映射被引用则抛出异常
     */
    private void checkMappingReference(Long tenantId, Long treeNodeId, Long categoryId) {
        // 1. 检查工料机项是否引用了这个映射关系
        Long resourceItemCount = resourceItemMapper.selectCount(
                new LambdaQueryWrapper<ResourceItemDO>()
                        .eq(ResourceItemDO::getTenantId, tenantId)
                        .eq(ResourceItemDO::getCategoryTreeId, treeNodeId)
                        .eq(ResourceItemDO::getCategoryId, categoryId)
                        .eq(ResourceItemDO::getDeleted, 0)
        );
        
        if (resourceItemCount > 0) {
            throw exception0(BAD_REQUEST.getCode(), 
                    String.format("该类别映射已被 %d 个工料机项引用，无法删除关联", resourceItemCount));
        }
        
        // 2. 检查信息价工料机是否引用了这个映射关系
        Long infoPriceCount = infoPriceResourceMapper.selectCount(
                new LambdaQueryWrapper<InfoPriceResourceDO>()
                        .eq(InfoPriceResourceDO::getTenantId, tenantId)
                        .eq(InfoPriceResourceDO::getCategoryTreeId, treeNodeId)
                        .eq(InfoPriceResourceDO::getCategoryId, categoryId)
                        .eq(InfoPriceResourceDO::getDeleted, 0)
        );
        
        if (infoPriceCount > 0) {
            throw exception0(BAD_REQUEST.getCode(), 
                    String.format("该类别映射已被 %d 个信息价工料机引用，无法删除关联", infoPriceCount));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSortOrder(Long nodeId1, Long nodeId2) {
        Long tenantId = TenantContextHolder.getTenantId();
        
        // 1. 查询两个节点（使用行锁）
        ResourceCategoryTreeDO node1 = categoryTreeMapper.selectOne(
                new LambdaQueryWrapper<ResourceCategoryTreeDO>()
                        .eq(ResourceCategoryTreeDO::getId, nodeId1)
                        .eq(ResourceCategoryTreeDO::getTenantId, tenantId)
                        .last("FOR UPDATE")
        );
        ResourceCategoryTreeDO node2 = categoryTreeMapper.selectOne(
                new LambdaQueryWrapper<ResourceCategoryTreeDO>()
                        .eq(ResourceCategoryTreeDO::getId, nodeId2)
                        .eq(ResourceCategoryTreeDO::getTenantId, tenantId)
                        .last("FOR UPDATE")
        );
        
        if (node1 == null || node2 == null) {
            throw exception0(BAD_REQUEST.getCode(), "节点不存在");
        }
        
        // 2. 验证是否同级（parent_id 必须相同）
        if (!isSameParent(node1.getParentId(), node2.getParentId())) {
            throw exception0(BAD_REQUEST.getCode(), "只能在同级节点之间调整排序");
        }
        
        // 3. 交换 sort_order
        Integer tempSortOrder = node1.getSortOrder();
        node1.setSortOrder(node2.getSortOrder());
        node2.setSortOrder(tempSortOrder);
        
        // 4. 更新数据库
        categoryTreeMapper.updateById(node1);
        categoryTreeMapper.updateById(node2);
    }
    
    /**
     * 判断两个节点是否有相同的父节点
     */
    private boolean isSameParent(Long parentId1, Long parentId2) {
        if (parentId1 == null && parentId2 == null) {
            return true;
        }
        if (parentId1 == null || parentId2 == null) {
            return false;
        }
        return parentId1.equals(parentId2);
    }

    /**
     * 构建树形结构
     */
    private List<ResourceCategoryTreeNodeVO> buildTree(List<ResourceCategoryTreeDO> allNodes, Long parentId) {
        return allNodes.stream()
                .filter(node -> {
                    if (parentId == null) {
                        return node.getParentId() == null;
                    }
                    return parentId.equals(node.getParentId());
                })
                .map(node -> {
                    ResourceCategoryTreeNodeVO vo = BeanUtils.toBean(node, ResourceCategoryTreeNodeVO.class);
                    vo.setChildren(buildTree(allNodes, node.getId()));
                    return vo;
                })
                .collect(Collectors.toList());
    }
}
