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

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_CATALOG_CODE_DUPLICATE;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_CATALOG_HAS_CHILDREN;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_CATALOG_INVALID_NODE_TYPE;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_CATALOG_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_CATALOG_PARENT_NOT_EXISTS;
import static com.yhy.module.core.enums.ErrorCodeConstants.CALC_BASE_RATE_CATALOG_ROOT_EXISTS;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.yhy.module.core.controller.admin.calcbaserate.vo.CalcBaseRateCatalogRespVO;
import com.yhy.module.core.controller.admin.calcbaserate.vo.CalcBaseRateCatalogSaveReqVO;
import com.yhy.module.core.controller.admin.workbench.vo.calcbaserate.CalcBaseRateCatalogTreeRespVO;
import com.yhy.module.core.dal.dataobject.calcbaserate.CalcBaseRateCatalogDO;
import com.yhy.module.core.dal.mysql.calcbaserate.CalcBaseRateCatalogMapper;
import com.yhy.module.core.service.calcbaserate.CalcBaseRateCatalogService;
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 CalcBaseRateCatalogServiceImpl implements CalcBaseRateCatalogService {

    @Resource
    private CalcBaseRateCatalogMapper calcBaseRateCatalogMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCalcBaseRateCatalog(CalcBaseRateCatalogSaveReqVO createReqVO) {
        // 1. 校验编码唯一性
        validateCodeUnique(null, createReqVO.getCode());

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

        // 3. 校验根节点唯一性
        if (CalcBaseRateCatalogDO.NODE_TYPE_ROOT.equals(createReqVO.getNodeType())) {
            validateRootUnique();
        }

        // 4. 构建DO对象
        CalcBaseRateCatalogDO calcBaseRateCatalog = buildCalcBaseRateCatalogDO(createReqVO);

        // 5. 设置排序
        if (calcBaseRateCatalog.getSortOrder() == null) {
            calcBaseRateCatalog.setSortOrder(getNextSortOrder(createReqVO.getParentId()));
        }

        // 6. 预设level和临时path
        CalcBaseRateCatalogDO parent = null;
        if (createReqVO.getParentId() != null) {
            parent = calcBaseRateCatalogMapper.selectById(createReqVO.getParentId());
            if (parent == null) {
                throw exception(CALC_BASE_RATE_CATALOG_PARENT_NOT_EXISTS);
            }
            calcBaseRateCatalog.setLevel(parent.getLevel() + 1);
            calcBaseRateCatalog.setPath(parent.getPath());
        } else {
            calcBaseRateCatalog.setLevel(0);
            calcBaseRateCatalog.setPath(new String[]{});
        }

        // 7. 插入数据库获取ID
        calcBaseRateCatalogMapper.insert(calcBaseRateCatalog);

        // 8. 更新真实路径
        String[] realPath = parent == null
            ? new String[]{calcBaseRateCatalog.getId().toString()}
            : ArrayUtil.append(parent.getPath(), calcBaseRateCatalog.getId().toString());
        calcBaseRateCatalog.setPath(realPath);
        calcBaseRateCatalogMapper.updateById(calcBaseRateCatalog);

        return calcBaseRateCatalog.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCalcBaseRateCatalog(CalcBaseRateCatalogSaveReqVO updateReqVO) {
        // 1. 校验节点存在
        validateCalcBaseRateCatalogExists(updateReqVO.getId());

        // 2. 校验编码唯一性
        if (updateReqVO.getCode() != null) {
            validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
        }

        // 3. 校验节点类型
        if (updateReqVO.getNodeType() != null) {
            validateNodeType(updateReqVO.getNodeType());
        }

        // 4. 构建更新对象
        CalcBaseRateCatalogDO updateObj = CalcBaseRateCatalogDO.builder()
                .id(updateReqVO.getId())
                .code(updateReqVO.getCode())
                .name(updateReqVO.getName())
                .nodeType(updateReqVO.getNodeType())
                .sortOrder(updateReqVO.getSortOrder())
                .attributes(updateReqVO.getAttributes())
                .build();

        // 5. 更新数据库
        calcBaseRateCatalogMapper.updateById(updateObj);
    }

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

        // 2. 校验是否有子节点
        List<CalcBaseRateCatalogDO> children = calcBaseRateCatalogMapper.selectListByParentId(id);
        if (CollUtil.isNotEmpty(children)) {
            throw exception(CALC_BASE_RATE_CATALOG_HAS_CHILDREN);
        }

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

    @Override
    public CalcBaseRateCatalogRespVO getCalcBaseRateCatalog(Long id) {
        CalcBaseRateCatalogDO item = calcBaseRateCatalogMapper.selectById(id);
        if (item == null) {
            return null;
        }
        return convertToRespVO(item);
    }

    @Override
    public List<CalcBaseRateCatalogRespVO> getCalcBaseRateCatalogTree() {
        // 1. 查询所有节点
        List<CalcBaseRateCatalogDO> allItems = calcBaseRateCatalogMapper.selectList();

        // 2. 转换为VO
        List<CalcBaseRateCatalogRespVO> allVOs = allItems.stream()
                .map(this::convertToRespVO)
                .collect(Collectors.toList());

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

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

    private void validateCodeUnique(Long id, String code) {
        CalcBaseRateCatalogDO existing = calcBaseRateCatalogMapper.selectByCode(code);
        if (existing != null && !existing.getId().equals(id)) {
            throw exception(CALC_BASE_RATE_CATALOG_CODE_DUPLICATE);
        }
    }

    private void validateNodeType(String nodeType) {
        List<String> validTypes = Arrays.asList(
                CalcBaseRateCatalogDO.NODE_TYPE_ROOT,
                CalcBaseRateCatalogDO.NODE_TYPE_PROVINCE,
                CalcBaseRateCatalogDO.NODE_TYPE_CONTENT
        );
        if (!validTypes.contains(nodeType)) {
            throw exception(CALC_BASE_RATE_CATALOG_INVALID_NODE_TYPE);
        }
    }

    private void validateRootUnique() {
        CalcBaseRateCatalogDO existingRoot = calcBaseRateCatalogMapper.selectByNodeType(CalcBaseRateCatalogDO.NODE_TYPE_ROOT);
        if (existingRoot != null) {
            throw exception(CALC_BASE_RATE_CATALOG_ROOT_EXISTS);
        }
    }

    private CalcBaseRateCatalogDO validateCalcBaseRateCatalogExists(Long id) {
        CalcBaseRateCatalogDO item = calcBaseRateCatalogMapper.selectById(id);
        if (item == null) {
            throw exception(CALC_BASE_RATE_CATALOG_NOT_EXISTS);
        }
        return item;
    }

    private CalcBaseRateCatalogDO buildCalcBaseRateCatalogDO(CalcBaseRateCatalogSaveReqVO reqVO) {
        return CalcBaseRateCatalogDO.builder()
                .id(reqVO.getId())
                .parentId(reqVO.getParentId())
                .code(reqVO.getCode())
                .name(reqVO.getName())
                .nodeType(reqVO.getNodeType())
                .sortOrder(reqVO.getSortOrder())
                .attributes(reqVO.getAttributes())
                .build();
    }

    private Integer getNextSortOrder(Long parentId) {
        List<CalcBaseRateCatalogDO> siblings = calcBaseRateCatalogMapper.selectSiblings(parentId);
        if (CollUtil.isEmpty(siblings)) {
            return 1;
        }
        return siblings.stream()
                .mapToInt(CalcBaseRateCatalogDO::getSortOrder)
                .max()
                .orElse(0) + 1;
    }

    private CalcBaseRateCatalogRespVO convertToRespVO(CalcBaseRateCatalogDO item) {
        CalcBaseRateCatalogRespVO vo = new CalcBaseRateCatalogRespVO();
        vo.setId(item.getId());
        vo.setParentId(item.getParentId());
        vo.setCode(item.getCode());
        vo.setName(item.getName());
        vo.setNodeType(item.getNodeType());
        vo.setSortOrder(item.getSortOrder());
        vo.setPath(item.getPath());
        vo.setLevel(item.getLevel());
        vo.setAttributes(item.getAttributes());
        vo.setCreateTime(item.getCreateTime());
        vo.setUpdateTime(item.getUpdateTime());
        return vo;
    }

    @Override
    public List<CalcBaseRateCatalogTreeRespVO> getCalcBaseRateCatalogTreeForWorkbench() {
        // 1. 查询所有节点
        List<CalcBaseRateCatalogDO> allItems = calcBaseRateCatalogMapper.selectList();

        // 2. 转换为工作台VO
        List<CalcBaseRateCatalogTreeRespVO> allVOs = allItems.stream()
                .map(this::convertToWorkbenchRespVO)
                .collect(Collectors.toList());

        // 3. 构建树形结构
        return buildWorkbenchTree(allVOs, null);
    }

    private List<CalcBaseRateCatalogRespVO> buildTree(List<CalcBaseRateCatalogRespVO> allNodes, Long parentId) {
        List<CalcBaseRateCatalogRespVO> result = new ArrayList<>();
        for (CalcBaseRateCatalogRespVO node : allNodes) {
            if ((parentId == null && node.getParentId() == null)
                    || (parentId != null && parentId.equals(node.getParentId()))) {
                node.setChildren(buildTree(allNodes, node.getId()));
                result.add(node);
            }
        }
        return result;
    }

    private CalcBaseRateCatalogTreeRespVO convertToWorkbenchRespVO(CalcBaseRateCatalogDO item) {
        CalcBaseRateCatalogTreeRespVO vo = new CalcBaseRateCatalogTreeRespVO();
        vo.setId(item.getId());
        vo.setParentId(item.getParentId());
        vo.setCode(item.getCode());
        vo.setName(item.getName());
        vo.setNodeType(item.getNodeType());
        return vo;
    }

    private List<CalcBaseRateCatalogTreeRespVO> buildWorkbenchTree(List<CalcBaseRateCatalogTreeRespVO> allNodes, Long parentId) {
        List<CalcBaseRateCatalogTreeRespVO> result = new ArrayList<>();
        for (CalcBaseRateCatalogTreeRespVO node : allNodes) {
            if ((parentId == null && node.getParentId() == null)
                    || (parentId != null && parentId.equals(node.getParentId()))) {
                node.setChildren(buildWorkbenchTree(allNodes, node.getId()));
                result.add(node);
            }
        }
        return result;
    }
}
