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.QuotaItemRespVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaItemSaveReqVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceRespVO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaItemDO;
import com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO;
import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaItemMapper;
import com.yhy.module.core.dal.mysql.quota.QuotaResourceMapper;
import com.yhy.module.core.enums.ErrorCodeConstants;
import com.yhy.module.core.service.quota.QuotaItemService;
import com.yhy.module.core.service.quota.QuotaResourceService;
import java.math.BigDecimal;
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 QuotaItemServiceImpl implements QuotaItemService {

    @Resource
    private QuotaItemMapper quotaItemMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private com.yhy.module.core.dal.mysql.quota.QuotaCatalogTreeMapper quotaCatalogTreeMapper;

    @Resource
    private QuotaResourceMapper quotaResourceMapper;

    @Resource
    private QuotaResourceService quotaResourceService;

    @Resource
    private com.yhy.module.core.dal.mysql.quota.QuotaMarketMaterialMapper quotaMarketMaterialMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createQuotaItem(QuotaItemSaveReqVO createReqVO) {
        // 验证定额条目存在且是内容节点
        QuotaCatalogTreeDO catalogItem = validateCatalogItem(createReqVO.getCatalogItemId());

        // 转换为 DO
        QuotaItemDO quotaItem = convertToDO(createReqVO);

        // 插入
        quotaItemMapper.insert(quotaItem);

        // 如果有工料机组成，批量添加
        if (createReqVO.getResources() != null && !createReqVO.getResources().isEmpty()) {
            quotaResourceService.batchCreateQuotaResources(quotaItem.getId(), createReqVO.getResources());

            // 计算基价
            calculateBasePrice(quotaItem.getId());
        }

        return quotaItem.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateQuotaItem(QuotaItemSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());

        // 转换为 DO
        QuotaItemDO updateObj = convertToDO(updateReqVO);
        updateObj.setId(updateReqVO.getId());

        // 更新
        quotaItemMapper.updateById(updateObj);

        // 如果有工料机组成，先删除旧的，再添加新的
        if (updateReqVO.getResources() != null) {
            quotaResourceMapper.deleteByQuotaItemId(updateReqVO.getId());
            if (!updateReqVO.getResources().isEmpty()) {
                quotaResourceService.batchCreateQuotaResources(updateReqVO.getId(), updateReqVO.getResources());
            }

            // 重新计算基价
            calculateBasePrice(updateReqVO.getId());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteQuotaItem(Long id) {
        // 校验存在
        validateExists(id);

        // 校验是否存在工料机组成
        Long resourceCount = quotaResourceMapper.selectCount(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO>()
                        .eq(com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO::getQuotaItemId, id));
        if (resourceCount > 0) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_ITEM_HAS_RESOURCES);
        }

        // 校验是否存在市场主材设备
        Long marketMaterialCount = quotaMarketMaterialMapper.selectCount(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.yhy.module.core.dal.dataobject.quota.QuotaMarketMaterialDO>()
                        .eq(com.yhy.module.core.dal.dataobject.quota.QuotaMarketMaterialDO::getQuotaItemId, id));
        if (marketMaterialCount > 0) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_ITEM_HAS_MARKET_MATERIALS);
        }

        // 删除定额基价
        quotaItemMapper.deleteById(id);
    }

    @Override
    public QuotaItemDO getQuotaItem(Long id) {
        return quotaItemMapper.selectById(id);
    }

    @Override
    public QuotaItemRespVO getQuotaItemDetail(Long id) {
        QuotaItemDO quotaItem = validateExists(id);

        // 转换为 VO
        QuotaItemRespVO vo = convertToVO(quotaItem);

        // 查询工料机组成
        List<QuotaResourceRespVO> resources = quotaResourceService.getQuotaResourceList(id);
        vo.setResources(resources);
        
        // 计算四个价格虚拟字段（从工料机合价总和计算）
        calculateQuotaItemPricesFromResources(vo, resources);

        return vo;
    }

    @Override
    public List<QuotaItemRespVO> getQuotaItemList(Long catalogItemId) {
        List<QuotaItemDO> list = quotaItemMapper.selectListByCatalogItemId(catalogItemId);
        List<QuotaItemRespVO> result = list.stream().map(this::convertToVO).collect(Collectors.toList());
        
        // 为每个定额基价计算四个价格虚拟字段
        for (QuotaItemRespVO vo : result) {
            calculateQuotaItemPrices(vo);
        }
        
        return result;
    }
    
    /**
     * 从已查询的工料机列表计算四个价格虚拟字段
     * 避免重复查询工料机数据
     */
    private void calculateQuotaItemPricesFromResources(QuotaItemRespVO vo, List<QuotaResourceRespVO> resources) {
        if (vo == null || resources == null) {
            return;
        }
        
        // 计算四个价格字段的总和
        BigDecimal taxExclBaseTotal = BigDecimal.ZERO;
        BigDecimal taxInclBaseTotal = BigDecimal.ZERO;
        BigDecimal taxExclCompileTotal = BigDecimal.ZERO;
        BigDecimal taxInclCompileTotal = BigDecimal.ZERO;
        
        for (QuotaResourceRespVO resource : resources) {
            if (resource.getTaxExclBaseTotalSum() != null) {
                taxExclBaseTotal = taxExclBaseTotal.add(resource.getTaxExclBaseTotalSum());
            }
            if (resource.getTaxInclBaseTotalSum() != null) {
                taxInclBaseTotal = taxInclBaseTotal.add(resource.getTaxInclBaseTotalSum());
            }
            if (resource.getTaxExclCompileTotalSum() != null) {
                taxExclCompileTotal = taxExclCompileTotal.add(resource.getTaxExclCompileTotalSum());
            }
            if (resource.getTaxInclCompileTotalSum() != null) {
                taxInclCompileTotal = taxInclCompileTotal.add(resource.getTaxInclCompileTotalSum());
            }
        }
        
        // 设置虚拟字段
        vo.setTaxExclBasePrice(taxExclBaseTotal);
        vo.setTaxInclBasePrice(taxInclBaseTotal);
        vo.setTaxExclCompilePrice(taxExclCompileTotal);
        vo.setTaxInclCompilePrice(taxInclCompileTotal);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calculateBasePrice(Long id) {
        QuotaItemDO quotaItem = validateExists(id);

        // 查询工料机组成
        List<QuotaResourceDO> resources = quotaResourceMapper.selectListByQuotaItemId(id);

        if (resources.isEmpty()) {
            log.warn("[calculateBasePrice] 定额基价没有工料机组成，无法计算基价，quotaItemId={}", id);
            return;
        }

        // 计算基价：Σ(price * dosage * (1 + loss_rate))
        BigDecimal basePrice = BigDecimal.ZERO;
        BigDecimal laborCost = BigDecimal.ZERO;
        BigDecimal materialCost = BigDecimal.ZERO;
        BigDecimal machineCost = BigDecimal.ZERO;

        for (QuotaResourceDO resource : resources) {
            BigDecimal price = resource.getPrice();
            if (price == null) {
                log.warn("[calculateBasePrice] 工料机价格为空，跳过，resourceId={}", resource.getResourceItemId());
                continue;
            }

            // 计算实际消耗量（含损耗）
            BigDecimal actualDosage = resource.getActualDosage();

            // 计算金额
            BigDecimal amount = price.multiply(actualDosage);
            basePrice = basePrice.add(amount);

            // 根据资源类型累加费用
            String resourceType = resource.getAttributes() != null ?
                    (String) resource.getAttributes().get("resource_type") : null;
            if ("labor".equals(resourceType)) {
                laborCost = laborCost.add(amount);
            } else if ("material".equals(resourceType)) {
                materialCost = materialCost.add(amount);
            } else if ("machine".equals(resourceType)) {
                machineCost = machineCost.add(amount);
            }
        }

        // 更新定额基价
        quotaItem.setBasePrice(basePrice);
        quotaItem.setLaborCost(laborCost);
        quotaItem.setMaterialCost(materialCost);
        quotaItem.setMachineCost(machineCost);

        quotaItemMapper.updateById(quotaItem);

        log.info("[calculateBasePrice] 计算基价成功，quotaItemId={}, basePrice={}", id, basePrice);
    }

    @Override
    public Long getCategoryTreeIdByQuotaItem(Long quotaItemId) {
        // 1. 获取定额基价
        QuotaItemDO quotaItem = validateExists(quotaItemId);

        // 2. 获取定额基价树节点（第二层）
        com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTreeNode = quotaCatalogTreeMapper.selectById(quotaItem.getCatalogItemId());
        if (catalogTreeNode == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_TREE_NOT_EXISTS);
        }

        // 3. 获取定额专业节点（第一层）
        QuotaCatalogItemDO specialtyNode = quotaCatalogItemMapper.selectById(catalogTreeNode.getCatalogItemId());
        if (specialtyNode == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }

        // 4. 返回绑定的工料机专业ID
        if (specialtyNode.getCategoryTreeId() == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_SPECIALTY_NOT_BOUND);
        }

        return specialtyNode.getCategoryTreeId();
    }

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

    private QuotaItemDO validateExists(Long id) {
        QuotaItemDO quotaItem = quotaItemMapper.selectById(id);
        if (quotaItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_ITEM_NOT_EXISTS);
        }
        return quotaItem;
    }

    private com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO validateCatalogItem(Long catalogItemId) {
        com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTree = quotaCatalogTreeMapper.selectById(catalogItemId);
        if (catalogTree == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_TREE_NOT_EXISTS);
        }

        // 验证是内容节点
        if (!"content".equals(catalogTree.getContentType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_CONTENT);
        }

        return catalogTree;
    }

    /**
     * 向上追溯到定额专业节点
     */
    private QuotaCatalogItemDO findSpecialtyNode(QuotaCatalogItemDO node) {
        // 如果当前节点就是定额专业节点
        if ("specialty".equals(node.getNodeType())) {
            return node;
        }

        // 如果有父节点，继续向上查找
        if (node.getParentId() != null) {
            QuotaCatalogItemDO parent = quotaCatalogItemMapper.selectById(node.getParentId());
            if (parent != null) {
                return findSpecialtyNode(parent);
            }
        }

        // 尝试从 path 数组中查找
        if (node.getPath() != null && node.getPath().length > 0) {
            for (String pathId : node.getPath()) {
                QuotaCatalogItemDO pathNode = quotaCatalogItemMapper.selectById(Long.parseLong(pathId));
                if (pathNode != null && "specialty".equals(pathNode.getNodeType())) {
                    return pathNode;
                }
            }
        }

        throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_SPECIALTY_NOT_FOUND);
    }

    private QuotaItemDO convertToDO(QuotaItemSaveReqVO vo) {
        QuotaItemDO quotaItem = new QuotaItemDO();
        quotaItem.setCatalogItemId(vo.getCatalogItemId());
        quotaItem.setCode(vo.getCode());
        quotaItem.setName(vo.getName());
        quotaItem.setUnit(vo.getUnit());
        quotaItem.setBasePrice(vo.getBasePrice());
        quotaItem.setAmount(vo.getAmount());
        quotaItem.setAttributes(vo.getAttributes());
        return quotaItem;
    }

    private QuotaItemRespVO convertToVO(QuotaItemDO quotaItem) {
        QuotaItemRespVO vo = new QuotaItemRespVO();
        vo.setId(quotaItem.getId());
        vo.setCatalogItemId(quotaItem.getCatalogItemId());
        vo.setCode(quotaItem.getCode());
        vo.setName(quotaItem.getName());
        vo.setUnit(quotaItem.getUnit());
        vo.setBasePrice(quotaItem.getBasePrice());
        vo.setAmount(quotaItem.getAmount());
        vo.setAttributes(quotaItem.getAttributes());
        vo.setCreateTime(quotaItem.getCreateTime());
        vo.setUpdateTime(quotaItem.getUpdateTime());

        // 扩展字段
        vo.setLaborCost(quotaItem.getLaborCost());
        vo.setMaterialCost(quotaItem.getMaterialCost());
        vo.setMachineCost(quotaItem.getMachineCost());
        vo.setVersionNo(quotaItem.getVersionNo());
        vo.setRemark(quotaItem.getRemark());

        // 四个价格字段改为虚拟字段，由 calculateQuotaItemPrices 方法动态计算
        // 这里先设置为 null，后续在 getQuotaItemList 和 getQuotaItemDetail 中计算
        vo.setTaxExclBasePrice(null);
        vo.setTaxInclBasePrice(null);
        vo.setTaxExclCompilePrice(null);
        vo.setTaxInclCompilePrice(null);

        return vo;
    }
    
    /**
     * 计算定额基价的四个价格虚拟字段
     * 数据来源于子目工料机的合价总和
     */
    private void calculateQuotaItemPrices(QuotaItemRespVO vo) {
        if (vo == null || vo.getId() == null) {
            return;
        }
        
        // 查询该定额基价的所有工料机组成
        List<QuotaResourceRespVO> resources = quotaResourceService.getQuotaResourceList(vo.getId());
        
        // 计算四个价格字段的总和
        BigDecimal taxExclBaseTotal = BigDecimal.ZERO;
        BigDecimal taxInclBaseTotal = BigDecimal.ZERO;
        BigDecimal taxExclCompileTotal = BigDecimal.ZERO;
        BigDecimal taxInclCompileTotal = BigDecimal.ZERO;
        
        for (QuotaResourceRespVO resource : resources) {
            if (resource.getTaxExclBaseTotalSum() != null) {
                taxExclBaseTotal = taxExclBaseTotal.add(resource.getTaxExclBaseTotalSum());
            }
            if (resource.getTaxInclBaseTotalSum() != null) {
                taxInclBaseTotal = taxInclBaseTotal.add(resource.getTaxInclBaseTotalSum());
            }
            if (resource.getTaxExclCompileTotalSum() != null) {
                taxExclCompileTotal = taxExclCompileTotal.add(resource.getTaxExclCompileTotalSum());
            }
            if (resource.getTaxInclCompileTotalSum() != null) {
                taxInclCompileTotal = taxInclCompileTotal.add(resource.getTaxInclCompileTotalSum());
            }
        }
        
        // 设置虚拟字段
        vo.setTaxExclBasePrice(taxExclBaseTotal);
        vo.setTaxInclBasePrice(taxInclBaseTotal);
        vo.setTaxExclCompilePrice(taxExclCompileTotal);
        vo.setTaxInclCompilePrice(taxInclCompileTotal);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Deprecated
    public void updateQuotaItemPrices(Long quotaItemId, BigDecimal taxExclBasePrice,
                                      BigDecimal taxInclBasePrice, BigDecimal taxExclCompilePrice, BigDecimal taxInclCompilePrice) {
        // 注意：四个价格字段现在是虚拟字段，由 calculateQuotaItemPrices 方法在查询时动态计算
        // 此方法已废弃，不再更新数据库
        log.warn("[updateQuotaItemPrices] 此方法已废弃，四个价格字段现在是虚拟字段，quotaItemId={}", quotaItemId);
    }

    @Override
    public List<QuotaItemRespVO> getQuotaItemByCode(String code) {
        // 根据编码查询定额基价（编码存储在 attributes->code 中）
        List<QuotaItemDO> items = quotaItemMapper.selectByCode(code);
        
        return items.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    @Override
    public Long getRateModeIdByQuotaItem(Long quotaItemId) {
        // 1. 获取定额基价
        QuotaItemDO quotaItem = validateExists(quotaItemId);

        // 2. 获取定额基价树节点（第二层）
        QuotaCatalogTreeDO catalogTreeNode = quotaCatalogTreeMapper.selectById(quotaItem.getCatalogItemId());
        if (catalogTreeNode == null) {
            log.warn("[getRateModeIdByQuotaItem] 定额基价树节点不存在, quotaItemId={}, catalogItemId={}", 
                    quotaItemId, quotaItem.getCatalogItemId());
            return null;
        }

        // 3. 获取定额专业节点（第一层）
        QuotaCatalogItemDO specialtyNode = quotaCatalogItemMapper.selectById(catalogTreeNode.getCatalogItemId());
        if (specialtyNode == null) {
            log.warn("[getRateModeIdByQuotaItem] 定额专业节点不存在, quotaItemId={}, catalogItemId={}", 
                    quotaItemId, catalogTreeNode.getCatalogItemId());
            return null;
        }

        // 4. 查找该专业节点下的费率模式子节点
        List<QuotaCatalogItemDO> children = quotaCatalogItemMapper.selectListByParentId(specialtyNode.getId());
        for (QuotaCatalogItemDO child : children) {
            if ("rate_mode".equals(child.getNodeType())) {
                return child.getId();
            }
        }

        // 5. 如果没有找到费率模式节点，返回null
        log.warn("[getRateModeIdByQuotaItem] 未找到费率模式节点, quotaItemId={}, specialtyNodeId={}", 
                quotaItemId, specialtyNode.getId());
        return null;
    }

    @Override
    public Long getSpecialtyIdByQuotaItem(Long quotaItemId) {
        if (quotaItemId == null) {
            return null;
        }
        
        // 1. 获取定额基价
        QuotaItemDO quotaItem = quotaItemMapper.selectById(quotaItemId);
        if (quotaItem == null) {
            log.warn("[getSpecialtyIdByQuotaItem] 定额基价不存在, quotaItemId={}", quotaItemId);
            return null;
        }

        // 2. 获取定额基价树节点（第二层）
        QuotaCatalogTreeDO catalogTreeNode = quotaCatalogTreeMapper.selectById(quotaItem.getCatalogItemId());
        if (catalogTreeNode == null) {
            log.warn("[getSpecialtyIdByQuotaItem] 定额基价树节点不存在, quotaItemId={}, catalogItemId={}", 
                    quotaItemId, quotaItem.getCatalogItemId());
            return null;
        }

        // 3. 获取定额专业节点（第一层）
        QuotaCatalogItemDO specialtyNode = quotaCatalogItemMapper.selectById(catalogTreeNode.getCatalogItemId());
        if (specialtyNode == null) {
            log.warn("[getSpecialtyIdByQuotaItem] 定额专业节点不存在, quotaItemId={}, catalogItemId={}", 
                    quotaItemId, catalogTreeNode.getCatalogItemId());
            return null;
        }

        // 4. 如果当前节点不是专业节点，向上追溯
        if (!"specialty".equals(specialtyNode.getNodeType())) {
            return findSpecialtyNodeId(specialtyNode);
        }

        return specialtyNode.getId();
    }

    /**
     * 向上追溯到定额专业节点
     */
    private Long findSpecialtyNodeId(QuotaCatalogItemDO node) {
        if (node == null) {
            return null;
        }
        
        // 如果当前节点就是定额专业节点
        if ("specialty".equals(node.getNodeType())) {
            return node.getId();
        }

        // 如果有父节点，继续向上查找
        if (node.getParentId() != null) {
            QuotaCatalogItemDO parent = quotaCatalogItemMapper.selectById(node.getParentId());
            if (parent != null) {
                return findSpecialtyNodeId(parent);
            }
        }

        // 尝试从 path 数组中查找
        if (node.getPath() != null && node.getPath().length > 0) {
            for (String pathId : node.getPath()) {
                QuotaCatalogItemDO pathNode = quotaCatalogItemMapper.selectById(Long.parseLong(pathId));
                if (pathNode != null && "specialty".equals(pathNode.getNodeType())) {
                    return pathNode.getId();
                }
            }
        }

        log.warn("[findSpecialtyNodeId] 未找到定额专业节点, nodeId={}", node.getId());
        return null;
    }
}
