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.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 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.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 定额子目 Service 实现类
 * 
 * @author yhy
 */
@Service
@Validated
@Slf4j
public class QuotaItemServiceImpl implements QuotaItemService {

    @Resource
    private QuotaItemMapper quotaItemMapper;

    @Resource
    private QuotaCatalogItemMapper quotaCatalogItemMapper;

    @Resource
    private QuotaResourceMapper quotaResourceMapper;

    @Resource
    private QuotaResourceService quotaResourceService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createQuotaItem(QuotaItemSaveReqVO createReqVO) {
        // 验证定额条目存在且是内容节点
        QuotaCatalogItemDO 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);
        
        // 删除工料机组成
        quotaResourceMapper.deleteByQuotaItemId(id);
        
        // 删除定额子目
        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);
        
        return vo;
    }

    @Override
    public List<QuotaItemRespVO> getQuotaItemList(Long catalogItemId) {
        List<QuotaItemDO> list = quotaItemMapper.selectListByCatalogItemId(catalogItemId);
        return list.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    @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. 获取定额条目（第三层）
        QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(quotaItem.getCatalogItemId());
        if (catalogItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }
        
        // 3. 向上追溯到定额专业节点（第一层）
        QuotaCatalogItemDO specialtyNode = findSpecialtyNode(catalogItem);
        
        // 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 QuotaCatalogItemDO validateCatalogItem(Long catalogItemId) {
        QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(catalogItemId);
        if (catalogItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS);
        }
        
        // 验证是内容节点
        if (!"content".equals(catalogItem.getContentType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_CONTENT);
        }
        
        return catalogItem;
    }

    /**
     * 向上追溯到定额专业节点
     */
    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.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.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());
        
        return vo;
    }
}
