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

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import com.yhy.module.core.controller.admin.resource.vo.ResourceItemQueryReqVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceItemSaveReqVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourcePriceSaveReqVO;
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.ResourceCategoryTreeMappingDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO;
import com.yhy.module.core.dal.dataobject.resource.ResourcePriceDO;
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.ResourceCategoryTreeMappingMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper;
import com.yhy.module.core.dal.mysql.resource.ResourcePriceMapper;
import com.yhy.module.core.service.resource.ResourceItemService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.format.DateTimeFormatter;
import java.util.List;

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

@Service
public class ResourceItemServiceImpl implements ResourceItemService {

    private static final DateTimeFormatter RANGE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Resource
    private ResourceCatalogItemMapper catalogItemMapper;
    @Resource
    private ResourceCategoryMapper categoryMapper;
    @Resource
    private ResourceCategoryTreeMappingMapper categoryTreeMappingMapper;
    @Resource
    private ResourceItemMapper resourceItemMapper;
    @Resource
    private ResourcePriceMapper resourcePriceMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createItem(ResourceItemSaveReqVO reqVO) {
        // 1. 验证目录树节点是否存在
        ResourceCatalogItemDO catalogItem = catalogItemMapper.selectById(reqVO.getCatalogItemId());
        if (catalogItem == null) {
            throw exception0(BAD_REQUEST.getCode(), "目录树节点不存在");
        }
        
        // 2. 验证 category_id 是否在机类树节点的允许范围内
        validateCategoryInAllowedRange(catalogItem.getCategoryTreeId(), reqVO.getCategoryId());
        
        // 3. 验证计算基数
        validateCalcBase(reqVO, catalogItem.getCategoryTreeId());
        
        // 4. 创建工料机项
        ResourceItemDO item = BeanUtils.toBean(reqVO, ResourceItemDO.class);
        item.setTenantId(TenantContextHolder.getTenantId());
        resourceItemMapper.insert(item);
        return item.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateItem(ResourceItemSaveReqVO reqVO) {
        // 1. 验证工料机项是否存在
        ResourceItemDO db = resourceItemMapper.selectById(reqVO.getId());
        if (db == null) {
            throw exception0(BAD_REQUEST.getCode(), "工料机项不存在");
        }
        
        // 2. 验证目录树节点是否存在
        ResourceCatalogItemDO catalogItem = catalogItemMapper.selectById(reqVO.getCatalogItemId());
        if (catalogItem == null) {
            throw exception0(BAD_REQUEST.getCode(), "目录树节点不存在");
        }
        
        // 3. 验证 category_id 是否在机类树节点的允许范围内
        validateCategoryInAllowedRange(catalogItem.getCategoryTreeId(), reqVO.getCategoryId());
        
        // 4. 验证计算基数
        validateCalcBase(reqVO, catalogItem.getCategoryTreeId());
        
        // 5. 更新工料机项
        ResourceItemDO item = BeanUtils.toBean(reqVO, ResourceItemDO.class);
        item.setTenantId(TenantContextHolder.getTenantId());
        resourceItemMapper.updateById(item);
    }

    @Override
    public void deleteItem(Long id) {
        resourceItemMapper.deleteById(id);
    }

    @Override
    public PageResult<ResourceItemRespVO> pageItems(ResourceItemQueryReqVO reqVO) {
        LambdaQueryWrapper<ResourceItemDO> wrapper = new LambdaQueryWrapper<ResourceItemDO>()
                .eq(ResourceItemDO::getTenantId, TenantContextHolder.getTenantId());
        
        // 按分类节点查询（包含子节点）
        if (reqVO.getCatalogItemId() != null) {
            // 查询该节点及其所有子节点
            ResourceCatalogItemDO catalogItem = catalogItemMapper.selectById(reqVO.getCatalogItemId());
            if (catalogItem != null) {
                // 使用 PostgreSQL 数组操作查询该节点及其子节点
                // path @> ARRAY[catalogItemId] 表示 path 包含该节点ID
                List<ResourceCatalogItemDO> nodeAndChildren = catalogItemMapper.selectList(
                    new LambdaQueryWrapper<ResourceCatalogItemDO>()
                        .eq(ResourceCatalogItemDO::getTenantId, TenantContextHolder.getTenantId())
                        .eq(ResourceCatalogItemDO::getCategoryTreeId, catalogItem.getCategoryTreeId())
                        .apply("path @> ARRAY[{0}]::text[]", reqVO.getCatalogItemId())
                );
                List<Long> itemIds = nodeAndChildren.stream()
                    .map(ResourceCatalogItemDO::getId)
                    .collect(java.util.stream.Collectors.toList());
                if (!itemIds.isEmpty()) {
                    wrapper.in(ResourceItemDO::getCatalogItemId, itemIds);
                } else {
                    // 没有匹配的节点，返回空结果
                    wrapper.eq(ResourceItemDO::getId, -1L);
                }
            }
        } else if (reqVO.getCategoryTreeId() != null) {
            // 按整个机类树查询
            wrapper.inSql(ResourceItemDO::getCatalogItemId,
                    "select id from yhy_catalog_item where category_tree_id = " + reqVO.getCategoryTreeId());
        }
        
        if (StringUtils.hasText(reqVO.getType())) {
            wrapper.eq(ResourceItemDO::getType, reqVO.getType());
        }
        if (StringUtils.hasText(reqVO.getKeyword())) {
            String like = "%" + reqVO.getKeyword() + "%";
            // 查询匹配的目录树条目 ID
            List<ResourceCatalogItemDO> matchedItems = catalogItemMapper.selectList(new LambdaQueryWrapper<ResourceCatalogItemDO>()
                    .eq(ResourceCatalogItemDO::getTenantId, TenantContextHolder.getTenantId())
                    .and(w -> w.like(ResourceCatalogItemDO::getName, like).or().like(ResourceCatalogItemDO::getCode, like)));
            
            // keyword 搜索范围：工料机名称、工料机单位、目录树名称、目录树编码
            if (!matchedItems.isEmpty()) {
                List<Long> matchedItemIds = matchedItems.stream()
                        .map(ResourceCatalogItemDO::getId)
                        .collect(java.util.stream.Collectors.toList());
                wrapper.and(w -> w.like(ResourceItemDO::getName, like)
                        .or().like(ResourceItemDO::getUnit, like)
                        .or().in(ResourceItemDO::getCatalogItemId, matchedItemIds));
            } else {
                wrapper.and(w -> w.like(ResourceItemDO::getName, like)
                        .or().like(ResourceItemDO::getUnit, like));
            }
        }
        wrapper.orderByDesc(ResourceItemDO::getId);
        Page<ResourceItemDO> page = resourceItemMapper.selectPage(new Page<>(reqVO.getPageNo(), reqVO.getPageSize()), wrapper);
        
        // 转换为 VO 并填充类别名称
        List<ResourceItemRespVO> records = page.getRecords().stream()
                .map(this::convertToRespVO)
                .collect(java.util.stream.Collectors.toList());
        
        return new PageResult<>(records, page.getTotal());
    }
    
    /**
     * 转换为响应 VO，并填充类别名称和机类树名称
     */
    private ResourceItemRespVO convertToRespVO(ResourceItemDO item) {
        ResourceItemRespVO vo = BeanUtils.toBean(item, ResourceItemRespVO.class);
        
        // 填充类别名称
        if (item.getCategoryId() != null) {
            ResourceCategoryDO category = categoryMapper.selectById(item.getCategoryId());
            if (category != null) {
                vo.setCategoryName(category.getName());
            }
        }
        
        // 填充工料机机类树名称
        if (item.getCategoryTreeId() != null) {
            // TODO: 需要注入 ResourceCategoryTreeMapper
            // ResourceCategoryTreeDO categoryTree = categoryTreeMapper.selectById(item.getCategoryTreeId());
            // if (categoryTree != null) {
            //     vo.setCategoryTreeName(categoryTree.getName());
            // }
        }
        
        return vo;
    }

    @Override
    public List<ResourcePriceDO> listPrices(Long resourceId) {
        return resourcePriceMapper.selectList(new LambdaQueryWrapper<ResourcePriceDO>()
                .eq(ResourcePriceDO::getResourceId, resourceId)
                .orderByDesc(ResourcePriceDO::getId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPrice(ResourcePriceSaveReqVO reqVO) {
        ResourceItemDO item = resourceItemMapper.selectById(reqVO.getResourceId());
        if (item == null) {
            throw exception0(BAD_REQUEST.getCode(), "资源项不存在");
        }
        ResourcePriceDO price = buildPriceDO(reqVO);
        price.setTenantId(TenantContextHolder.getTenantId());
        resourcePriceMapper.insert(price);
        return price.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePrice(ResourcePriceSaveReqVO reqVO) {
        ResourcePriceDO db = resourcePriceMapper.selectById(reqVO.getId());
        if (db == null) {
            throw exception0(BAD_REQUEST.getCode(), "价格不存在");
        }
        ResourcePriceDO price = buildPriceDO(reqVO);
        price.setTenantId(TenantContextHolder.getTenantId());
        resourcePriceMapper.updateById(price);
    }

    @Override
    public void deletePrice(Long id) {
        resourcePriceMapper.deleteById(id);
    }

    private ResourcePriceDO buildPriceDO(ResourcePriceSaveReqVO reqVO) {
        ResourcePriceDO price = BeanUtils.toBean(reqVO, ResourcePriceDO.class);
        price.setPricePeriod(String.format("[%s,%s)", RANGE_FMT.format(reqVO.getPeriodStart()), RANGE_FMT.format(reqVO.getPeriodEnd())));
        return price;
    }
    
    /**
     * 验证类别是否在机类树节点的允许范围内
     * 
     * @param categoryTreeId 机类树节点ID
     * @param categoryId 类别ID
     */
    private void validateCategoryInAllowedRange(Long categoryTreeId, Long categoryId) {
        Long tenantId = TenantContextHolder.getTenantId();
        
        // 查询该机类树节点是否关联了该类别
        Long count = categoryTreeMappingMapper.selectCount(
            new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                .eq(ResourceCategoryTreeMappingDO::getTenantId, tenantId)
                .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, categoryTreeId)
                .eq(ResourceCategoryTreeMappingDO::getCategoryId, categoryId)
        );
        
        if (count == 0) {
            throw exception0(BAD_REQUEST.getCode(), 
                String.format("类别ID %d 不在机类树节点 %d 的允许范围内，请先在机类树节点中添加该类别关联", 
                    categoryId, categoryTreeId));
        }
    }
    
    /**
     * 验证计算基数
     * 
     * @param reqVO 请求VO
     * @param categoryTreeId 机类树节点ID
     */
    private void validateCalcBase(ResourceItemSaveReqVO reqVO, Long categoryTreeId) {
        // 1. 如果单位不是 %，calcBase 必须为空
        if (!"%".equals(reqVO.getUnit())) {
            if (reqVO.getCalcBase() != null && !reqVO.getCalcBase().isEmpty()) {
                throw exception0(BAD_REQUEST.getCode(), "只有单位为 % 的工料机项才能设置计算基数");
            }
            return;
        }
        
        // 2. 如果单位是 %，验证 calcBase
        if (reqVO.getCalcBase() == null || reqVO.getCalcBase().isEmpty()) {
            // 允许为空，不强制要求
            return;
        }
        
        // 3. 解析 calcBase
        try {
            String formula = (String) reqVO.getCalcBase().get("formula");
            @SuppressWarnings("unchecked")
            java.util.Map<String, Object> variables = (java.util.Map<String, Object>) reqVO.getCalcBase().get("variables");
            
            if (formula == null || formula.trim().isEmpty()) {
                throw exception0(BAD_REQUEST.getCode(), "计算基数的公式不能为空");
            }
            
            if (variables == null || variables.isEmpty()) {
                throw exception0(BAD_REQUEST.getCode(), "计算基数的变量映射不能为空");
            }
            
            // 4. 获取允许的类别列表
            List<Long> allowedCategoryIds = getAllowedCategoryIds(categoryTreeId);
            
            // 5. 验证公式中的变量是否都在允许的类别列表中
            for (java.util.Map.Entry<String, Object> entry : variables.entrySet()) {
                Long categoryId = null;
                if (entry.getValue() instanceof Integer) {
                    categoryId = ((Integer) entry.getValue()).longValue();
                } else if (entry.getValue() instanceof Long) {
                    categoryId = (Long) entry.getValue();
                } else {
                    throw exception0(BAD_REQUEST.getCode(), 
                        String.format("变量 %s 的类别ID格式不正确", entry.getKey()));
                }
                
                if (!allowedCategoryIds.contains(categoryId)) {
                    throw exception0(BAD_REQUEST.getCode(), 
                        String.format("变量 %s 的类别ID %d 不在允许的类别范围内", entry.getKey(), categoryId));
                }
            }
            
            // 6. 验证公式语法（简单验证）
            validateFormulaSyntax(formula, variables.keySet());
            
        } catch (ClassCastException e) {
            throw exception0(BAD_REQUEST.getCode(), "计算基数格式不正确");
        }
    }
    
    /**
     * 获取允许的类别ID列表
     * 
     * @param categoryTreeId 机类树节点ID
     * @return 允许的类别ID列表
     */
    private List<Long> getAllowedCategoryIds(Long categoryTreeId) {
        Long tenantId = TenantContextHolder.getTenantId();
        
        List<ResourceCategoryTreeMappingDO> mappings = categoryTreeMappingMapper.selectList(
            new LambdaQueryWrapper<ResourceCategoryTreeMappingDO>()
                .eq(ResourceCategoryTreeMappingDO::getTenantId, tenantId)
                .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, categoryTreeId)
        );
        
        return mappings.stream()
            .map(ResourceCategoryTreeMappingDO::getCategoryId)
            .collect(java.util.stream.Collectors.toList());
    }
    
    /**
     * 验证公式语法
     * 
     * @param formula 公式
     * @param variableNames 变量名集合
     */
    private void validateFormulaSyntax(String formula, java.util.Set<String> variableNames) {
        // 1. 检查是否包含非法字符
        // 允许：数字、小数点、运算符(+-*/)、括号、空格、中文（变量名）
        if (!formula.matches("[0-9+\\-*/().\\s\\u4e00-\\u9fa5]+")) {
            throw exception0(BAD_REQUEST.getCode(), "公式包含非法字符，只允许数字、运算符(+-*/)、括号和变量名");
        }
        
        // 2. 检查括号是否匹配
        int count = 0;
        for (char c : formula.toCharArray()) {
            if (c == '(') count++;
            if (c == ')') count--;
            if (count < 0) {
                throw exception0(BAD_REQUEST.getCode(), "公式中的括号不匹配");
            }
        }
        if (count != 0) {
            throw exception0(BAD_REQUEST.getCode(), "公式中的括号不匹配");
        }
        
        // 3. 检查公式中的变量是否都在 variables 中定义
        for (String varName : variableNames) {
            if (!formula.contains(varName)) {
                throw exception0(BAD_REQUEST.getCode(), 
                    String.format("变量 %s 在公式中未使用", varName));
            }
        }
    }
}
