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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceTreeRespVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceTreeSaveReqVO;
import com.yhy.module.core.controller.admin.infoprice.vo.InfoPriceTreeSwapSortReqVO;
import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceTreeDO;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceBookMapper;
import com.yhy.module.core.dal.mysql.infoprice.InfoPriceTreeMapper;
import com.yhy.module.core.enums.infoprice.InfoPriceTypeEnum;
import com.yhy.module.core.service.infoprice.InfoPriceTreeService;
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.List;
import java.util.Map;
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 InfoPriceTreeServiceImpl implements InfoPriceTreeService {

    @Resource
    private InfoPriceTreeMapper infoPriceTreeMapper;

    @Resource
    private InfoPriceBookMapper infoPriceBookMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createInfoPriceTree(InfoPriceTreeSaveReqVO createReqVO) {
        // 1. 验证枚举类型
        validateEnumType(createReqVO.getEnumType());

        // 2. 获取租户ID（从上下文）
        Long tenantId = cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getTenantId();

        // 3. 验证编码唯一性
        validateCodeUnique(null, tenantId, createReqVO.getEnumType(), createReqVO.getCode());

        // 4. 验证父节点
        InfoPriceTreeDO parent = null;
        if (createReqVO.getParentId() != null) {
            parent = validateParentExists(createReqVO.getParentId());
            // 验证父节点的枚举类型一致
            if (!parent.getEnumType().equals(createReqVO.getEnumType())) {
                throw exception(INFO_PRICE_TREE_NOT_SAME_ENUM_TYPE);
            }
        }

        // 5. 构建DO对象
        InfoPriceTreeDO tree = BeanUtils.toBean(createReqVO, InfoPriceTreeDO.class);
        tree.setTenantId(tenantId);
        
        // 6. 计算路径
        if (parent == null) {
            tree.setPath(new String[]{});
        } else {
            String[] parentPath = parent.getPath();
            String[] newPath = ArrayUtil.append(parentPath, parent.getId().toString());
            tree.setPath(newPath);
        }

        // 7. 设置排序
        if (tree.getSortOrder() == null) {
            tree.setSortOrder(0);
        }

        // 8. 插入数据库
        infoPriceTreeMapper.insert(tree);
        return tree.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfoPriceTree(InfoPriceTreeSaveReqVO updateReqVO) {
        // 1. 验证节点存在
        InfoPriceTreeDO tree = validateTreeExists(updateReqVO.getId());

        // 2. 验证枚举类型
        validateEnumType(updateReqVO.getEnumType());

        // 3. 验证编码唯一性
        validateCodeUnique(updateReqVO.getId(), tree.getTenantId(), updateReqVO.getEnumType(), updateReqVO.getCode());

        // 4. 更新数据
        InfoPriceTreeDO updateObj = BeanUtils.toBean(updateReqVO, InfoPriceTreeDO.class);
        infoPriceTreeMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfoPriceTree(Long id) {
        // 1. 验证节点存在
        validateTreeExists(id);

        // 2. 验证是否有子节点
        Long childCount = infoPriceTreeMapper.countByParentId(id);
        if (childCount > 0) {
            throw exception(INFO_PRICE_TREE_HAS_CHILDREN);
        }

        // 3. 验证是否有信息价册
        Long bookCount = infoPriceBookMapper.countByTreeNodeId(id);
        if (bookCount > 0) {
            throw exception(INFO_PRICE_TREE_HAS_BOOKS);
        }

        // 4. 删除节点
        infoPriceTreeMapper.deleteById(id);
    }

    @Override
    public InfoPriceTreeRespVO getInfoPriceTree(Long id) {
        InfoPriceTreeDO tree = validateTreeExists(id);
        return BeanUtils.toBean(tree, InfoPriceTreeRespVO.class);
    }

    @Override
    public List<InfoPriceTreeRespVO> getInfoPriceTreeList(String enumType) {
        // 1. 验证枚举类型
        validateEnumType(enumType);

        // 2. 查询所有节点
        List<InfoPriceTreeDO> list = infoPriceTreeMapper.selectListByEnumType(enumType);
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }

        // 3. 转换为VO
        List<InfoPriceTreeRespVO> voList = BeanUtils.toBean(list, InfoPriceTreeRespVO.class);

        // 4. 构建树结构
        return buildTree(voList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapSort(InfoPriceTreeSwapSortReqVO reqVO) {
        // 1. 验证两个节点存在
        InfoPriceTreeDO node1 = validateTreeExists(reqVO.getNodeId1());
        InfoPriceTreeDO node2 = validateTreeExists(reqVO.getNodeId2());

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

        // 3. 验证是否同一枚举类型
        if (!node1.getEnumType().equals(node2.getEnumType())) {
            throw exception(INFO_PRICE_TREE_NOT_SAME_ENUM_TYPE);
        }

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

        node1.setSortOrder(sort2);
        node2.setSortOrder(sort1);

        infoPriceTreeMapper.updateById(node1);
        infoPriceTreeMapper.updateById(node2);
    }

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

    /**
     * 验证节点存在
     */
    private InfoPriceTreeDO validateTreeExists(Long id) {
        InfoPriceTreeDO tree = infoPriceTreeMapper.selectById(id);
        if (tree == null) {
            throw exception(INFO_PRICE_TREE_NOT_EXISTS);
        }
        return tree;
    }

    /**
     * 验证父节点存在
     */
    private InfoPriceTreeDO validateParentExists(Long parentId) {
        InfoPriceTreeDO parent = infoPriceTreeMapper.selectById(parentId);
        if (parent == null) {
            throw exception(INFO_PRICE_TREE_PARENT_NOT_EXISTS);
        }
        return parent;
    }

    /**
     * 验证编码唯一性
     */
    private void validateCodeUnique(Long id, Long tenantId, String enumType, String code) {
        InfoPriceTreeDO existing = infoPriceTreeMapper.selectByCode(tenantId, enumType, code);
        if (existing != null && !existing.getId().equals(id)) {
            throw exception(INFO_PRICE_TREE_CODE_DUPLICATE, code);
        }
    }

    /**
     * 验证枚举类型
     */
    private void validateEnumType(String enumType) {
        InfoPriceTypeEnum typeEnum = InfoPriceTypeEnum.valueOfCode(enumType);
        if (typeEnum == null) {
            throw exception(INFO_PRICE_TREE_INVALID_ENUM_TYPE);
        }
    }

    /**
     * 判断是否同级
     */
    private boolean isSameLevel(InfoPriceTreeDO node1, InfoPriceTreeDO node2) {
        // 父节点ID相同即为同级
        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<InfoPriceTreeRespVO> buildTree(List<InfoPriceTreeRespVO> list) {
        // 1. 按ID分组
        Map<Long, InfoPriceTreeRespVO> map = list.stream()
                .collect(Collectors.toMap(InfoPriceTreeRespVO::getId, v -> v));

        // 2. 构建父子关系
        List<InfoPriceTreeRespVO> roots = new ArrayList<>();
        for (InfoPriceTreeRespVO node : list) {
            if (node.getParentId() == null) {
                roots.add(node);
            } else {
                InfoPriceTreeRespVO parent = map.get(node.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(node);
                }
            }
        }

        return roots;
    }
}
