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

import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
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.dal.dataobject.quota.QuotaCatalogItemDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeDO;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMapper;
import com.yhy.module.core.enums.ErrorCodeConstants;
import com.yhy.module.core.service.quota.QuotaCatalogItemService;
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 定额目录条目 Service 实现类
 * 
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService {

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private ResourceCategoryTreeMapper resourceCategoryTreeMapper;

    @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 (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(Long parentId) {
        List<QuotaCatalogItemDO> list;
        if (parentId == null) {
            list = quotaCatalogItemMapper.selectRootList();
        } else {
            list = quotaCatalogItemMapper.selectListByParentId(parentId);
        }
        return list.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    @Override
    public List<QuotaCatalogItemRespVO> getQuotaCatalogItemTree() {
        // 查询所有根节点
        List<QuotaCatalogItemDO> rootList = quotaCatalogItemMapper.selectRootList();
        
        // 递归构建树
        return rootList.stream()
                .map(this::buildTree)
                .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 (!"type".equals(categoryTree.getNodeType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_TYPE);
        }
        
        // 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
    @Transactional(rollbackFor = Exception.class)
    public Long addDirectory(QuotaCatalogItemSaveReqVO createReqVO) {
        // 验证父节点
        if (createReqVO.getParentId() != null) {
            QuotaCatalogItemDO parent = validateExists(createReqVO.getParentId());
            if ("content".equals(parent.getContentType())) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_CONTENT_NO_CHILDREN);
            }
        }
        
        // 设置目录属性
        if (createReqVO.getAttributes() == null) {
            createReqVO.setAttributes(new HashMap<>());
        }
        createReqVO.getAttributes().put("content_type", "directory");
        createReqVO.getAttributes().put("allow_children", true);
        
        return createQuotaCatalogItem(createReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addContent(QuotaCatalogItemSaveReqVO createReqVO) {
        // 验证父节点必须是目录
        if (createReqVO.getParentId() == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_CONTENT_NEED_PARENT);
        }
        
        QuotaCatalogItemDO parent = validateExists(createReqVO.getParentId());
        if (!"directory".equals(parent.getContentType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_PARENT_NOT_DIRECTORY);
        }
        
        // 设置内容属性
        if (createReqVO.getAttributes() == null) {
            createReqVO.setAttributes(new HashMap<>());
        }
        createReqVO.getAttributes().put("content_type", "content");
        createReqVO.getAttributes().put("allow_children", false);
        
        return createQuotaCatalogItem(createReqVO);
    }

    @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);
        }
    }

    @Override
    public boolean hasChildren(Long id) {
        return quotaCatalogItemMapper.hasChildren(id);
    }

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

    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());
        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());
        vo.setHasChildren(hasChildren(catalogItem.getId()));
        
        return vo;
    }

    private QuotaCatalogItemRespVO buildTree(QuotaCatalogItemDO node) {
        QuotaCatalogItemRespVO vo = convertToVO(node);
        
        // 查询子节点
        List<QuotaCatalogItemDO> children = quotaCatalogItemMapper.selectListByParentId(node.getId());
        if (!children.isEmpty()) {
            vo.setChildren(children.stream()
                    .map(this::buildTree)
                    .collect(Collectors.toList()));
        }
        
        return vo;
    }
}
