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.QuotaResourceRespVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceSaveReqVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO;
import com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceMergedDO;
import com.yhy.module.core.dal.mysql.quota.QuotaResourceMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCatalogItemMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper;
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.context.annotation.Lazy;
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 QuotaResourceServiceImpl implements QuotaResourceService {

    @Resource
    private QuotaResourceMapper quotaResourceMapper;

    @Resource
    private ResourceItemMapper resourceItemMapper;

    @Resource
    private ResourceCatalogItemMapper resourceCatalogItemMapper;

    @Resource
    @Lazy
    private QuotaItemService quotaItemService;

    @Resource
    private com.yhy.module.core.dal.mysql.resource.ResourceMergedMapper resourceMergedMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createQuotaResource(QuotaResourceSaveReqVO createReqVO) {
        // 1. 验证工料机是否在范围内
        validateResourceInScope(createReqVO.getQuotaItemId(), createReqVO.getResourceItemId());
        
        // 2. 验证工料机是否存在
        ResourceItemDO resourceItem = resourceItemMapper.selectById(createReqVO.getResourceItemId());
        if (resourceItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS);
        }
        
        // 3. 转换为 DO
        QuotaResourceDO quotaResource = convertToDO(createReqVO);
        
        // 4. 插入（标准库不需要快照，查询时实时读取）
        quotaResourceMapper.insert(quotaResource);
        
        log.info("[createQuotaResource] 添加定额工料机组成成功，quotaItemId={}, resourceItemId={}", 
            createReqVO.getQuotaItemId(), createReqVO.getResourceItemId());
        
        return quotaResource.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateQuotaResource(QuotaResourceSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());
        
        // 验证工料机是否在范围内（如果修改了 resourceItemId）
        QuotaResourceDO existing = quotaResourceMapper.selectById(updateReqVO.getId());
        if (!existing.getResourceItemId().equals(updateReqVO.getResourceItemId())) {
            validateResourceInScope(updateReqVO.getQuotaItemId(), updateReqVO.getResourceItemId());
            
            // 验证新的工料机是否存在
            ResourceItemDO resourceItem = resourceItemMapper.selectById(updateReqVO.getResourceItemId());
            if (resourceItem == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS);
            }
        }
        
        // 转换为 DO
        QuotaResourceDO updateObj = convertToDO(updateReqVO);
        updateObj.setId(updateReqVO.getId());
        
        // 合并 attributes：保留原有字段，只更新传入的字段
        if (existing.getAttributes() != null) {
            java.util.Map<String, Object> mergedAttributes = new java.util.HashMap<>(existing.getAttributes());
            if (updateReqVO.getAttributes() != null) {
                mergedAttributes.putAll(updateReqVO.getAttributes());
            }
            updateObj.setAttributes(mergedAttributes);
        }
        
        // 更新（标准库不需要快照，查询时实时读取）
        quotaResourceMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteQuotaResource(Long id) {
        // 校验存在
        validateExists(id);
        
        // 删除
        quotaResourceMapper.deleteById(id);
    }

    @Override
    public QuotaResourceDO getQuotaResource(Long id) {
        return quotaResourceMapper.selectById(id);
    }

    @Override
    public List<QuotaResourceRespVO> getQuotaResourceList(Long quotaItemId) {
        List<QuotaResourceDO> list = quotaResourceMapper.selectListByQuotaItemId(quotaItemId);
        return list.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    @Override
    public List<ResourceItemRespVO> getAvailableResourceItems(Long quotaItemId) {
        // 1. 获取定额专业绑定的工料机专业ID
        Long categoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        
        // 2. 查询该工料机专业下的所有工料机数据
        List<ResourceItemDO> resourceItems = resourceItemMapper.selectByCategoryTreeId(categoryTreeId);
        
        // 3. 转换为 VO
        return resourceItems.stream()
            .map(this::convertResourceItemToVO)
            .collect(Collectors.toList());
    }

    @Override
    public List<ResourceItemRespVO> getAvailableResourceItemsWithFilter(Long quotaItemId, String code, String name, String spec) {
        // 1. 获取定额专业绑定的工料机专业ID
        Long categoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        
        // 2. 查询该工料机专业下的工料机数据（支持模糊查询）
        List<ResourceItemDO> resourceItems = resourceItemMapper.selectByCategoryTreeIdWithFilter(
            categoryTreeId, code, name, spec);
        
        // 3. 转换为 VO
        return resourceItems.stream()
            .map(this::convertResourceItemToVO)
            .collect(Collectors.toList());
    }

    @Override
    public void validateResourceInScope(Long quotaItemId, Long resourceItemId) {
        // 1. 获取定额专业绑定的工料机专业ID
        Long allowedCategoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        
        // 2. 获取工料机数据
        ResourceItemDO resourceItem = resourceItemMapper.selectById(resourceItemId);
        if (resourceItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS);
        }
        
        // 3. 获取工料机所属的目录节点（yhy_resource_catalog_item 表）
        com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO catalogItem = 
            resourceCatalogItemMapper.selectById(resourceItem.getCatalogItemId());
        if (catalogItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATALOG_ITEM_NOT_EXISTS);
        }
        
        // 4. 验证工料机是否在范围内
        if (!allowedCategoryTreeId.equals(catalogItem.getCategoryTreeId())) {
            log.warn("[validateResourceInScope] 工料机不在定额专业的数据范围内，" +
                "quotaItemId={}, resourceItemId={}, allowedCategoryTreeId={}, actualCategoryTreeId={}", 
                quotaItemId, resourceItemId, allowedCategoryTreeId, catalogItem.getCategoryTreeId());
            
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_RESOURCE_OUT_OF_SCOPE);
        }
        
        log.debug("[validateResourceInScope] 工料机在范围内，quotaItemId={}, resourceItemId={}", 
            quotaItemId, resourceItemId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchCreateQuotaResources(Long quotaItemId, List<QuotaResourceSaveReqVO> resources) {
        for (QuotaResourceSaveReqVO resource : resources) {
            resource.setQuotaItemId(quotaItemId);
            createQuotaResource(resource);
        }
    }

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

    private QuotaResourceDO validateExists(Long id) {
        QuotaResourceDO quotaResource = quotaResourceMapper.selectById(id);
        if (quotaResource == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_RESOURCE_NOT_EXISTS);
        }
        return quotaResource;
    }

    private QuotaResourceDO convertToDO(QuotaResourceSaveReqVO vo) {
        QuotaResourceDO quotaResource = new QuotaResourceDO();
        quotaResource.setQuotaItemId(vo.getQuotaItemId());
        quotaResource.setResourceItemId(vo.getResourceItemId());
        quotaResource.setDosage(vo.getDosage());
        quotaResource.setAttributes(vo.getAttributes());
        return quotaResource;
    }

    private QuotaResourceRespVO convertToVO(QuotaResourceDO quotaResource) {
        QuotaResourceRespVO vo = new QuotaResourceRespVO();
        vo.setId(quotaResource.getId());
        vo.setQuotaItemId(quotaResource.getQuotaItemId());
        vo.setResourceItemId(quotaResource.getResourceItemId());
        vo.setDosage(quotaResource.getDosage());
        vo.setAttributes(quotaResource.getAttributes());
        vo.setCreateTime(quotaResource.getCreateTime());
        vo.setUpdateTime(quotaResource.getUpdateTime());
        
        // 扩展字段
        vo.setLossRate(quotaResource.getLossRate());
        
        // 实时查询工料机信息（不使用快照）
        ResourceItemDO resourceItem = resourceItemMapper.selectById(quotaResource.getResourceItemId());
        if (resourceItem != null) {
            vo.setResourceCode(resourceItem.getCode());
            vo.setResourceName(resourceItem.getName());
            vo.setResourceUnit(resourceItem.getUnit());
            vo.setResourceSpec(resourceItem.getSpec());
            vo.setResourceCategoryId(resourceItem.getCategoryId());
            vo.setResourceType(resourceItem.getType());
            vo.setResourceTaxRate(resourceItem.getTaxRate());
            vo.setResourceTaxExclBasePrice(resourceItem.getTaxExclBasePrice());
            vo.setResourceTaxInclBasePrice(resourceItem.getTaxInclBasePrice());
            vo.setResourceTaxExclCompilePrice(resourceItem.getTaxExclCompilePrice());
            vo.setResourceTaxInclCompilePrice(resourceItem.getTaxInclCompilePrice());
            vo.setCalcBase(resourceItem.getCalcBase());
            
            // 价格优先使用除税基价
            if (resourceItem.getTaxExclBasePrice() != null) {
                vo.setPrice(resourceItem.getTaxExclBasePrice());
            } else if (resourceItem.getTaxInclBasePrice() != null) {
                vo.setPrice(resourceItem.getTaxInclBasePrice());
            }
            
            // 计算实际消耗量和金额
            vo.setActualDosage(quotaResource.getActualDosage());
            if (vo.getPrice() != null) {
                vo.setAmount(vo.getActualDosage().multiply(vo.getPrice()));
            }
            
            // 判断是否为复合工料机
            if (resourceItem.getIsMerged() != null && resourceItem.getIsMerged() == 1) {
                vo.setIsMerged(true);
                
                // 查询复合工料机的子数据
                List<ResourceMergedDO> mergedList = resourceMergedMapper.selectByMergedId(resourceItem.getId());
                
                if (mergedList != null && !mergedList.isEmpty()) {
                    List<QuotaResourceRespVO.MergedResourceItemVO> mergedItems = mergedList.stream()
                        .map(merged -> {
                            QuotaResourceRespVO.MergedResourceItemVO item = new QuotaResourceRespVO.MergedResourceItemVO();
                            item.setId(merged.getId());
                            item.setResourceItemId(merged.getSourceId());
                            item.setRegionCode(merged.getRegionCode());
                            item.setDosage(merged.getQuotaConsumption());
                            item.setPrice(merged.getTaxExclMarketPrice());
                            
                            // 查询源工料机信息
                            if (merged.getSourceId() != null) {
                                ResourceItemDO sourceItem = resourceItemMapper.selectById(merged.getSourceId());
                                if (sourceItem != null) {
                                    item.setResourceCode(sourceItem.getCode());
                                    item.setResourceName(sourceItem.getName());
                                    item.setResourceUnit(sourceItem.getUnit());
                                    item.setResourceSpec(sourceItem.getSpec());
                                    item.setResourceCategoryId(sourceItem.getCategoryId());
                                    item.setResourceType(sourceItem.getType());
                                    item.setResourceTaxRate(sourceItem.getTaxRate());
                                }
                            }
                            
                            // 计算实际消耗量和金额
                            if (merged.getQuotaConsumption() != null && quotaResource.getDosage() != null) {
                                // 实际消耗量 = 父定额消耗量 × 子定额消耗量 × (1 + 损耗率)
                                BigDecimal actualDosage = merged.getQuotaConsumption()
                                    .multiply(quotaResource.getDosage());
                                
                                if (quotaResource.getLossRate() != null) {
                                    actualDosage = actualDosage.multiply(
                                        BigDecimal.ONE.add(quotaResource.getLossRate())
                                    );
                                }
                                
                                item.setActualDosage(actualDosage);
                                
                                // 金额 = 实际消耗量 × 价格
                                if (merged.getTaxExclMarketPrice() != null) {
                                    item.setAmount(actualDosage.multiply(merged.getTaxExclMarketPrice()));
                                }
                            }
                            
                            return item;
                        })
                        .collect(Collectors.toList());
                    
                    vo.setMergedItems(mergedItems);
                }
            } else {
                vo.setIsMerged(false);
            }
        }
        
        return vo;
    }

    private ResourceItemRespVO convertResourceItemToVO(ResourceItemDO resourceItem) {
        ResourceItemRespVO vo = new ResourceItemRespVO();
        vo.setId(resourceItem.getId());
        vo.setCatalogItemId(resourceItem.getCatalogItemId());
        vo.setCode(resourceItem.getCode());
        vo.setName(resourceItem.getName());
        vo.setSpec(resourceItem.getSpec());
        vo.setType(resourceItem.getType());
        vo.setUnit(resourceItem.getUnit());
        vo.setCategoryId(resourceItem.getCategoryId());
        vo.setCategoryTreeId(resourceItem.getCategoryTreeId());
        vo.setTaxRate(resourceItem.getTaxRate());
        vo.setTaxExclBasePrice(resourceItem.getTaxExclBasePrice());
        vo.setTaxInclBasePrice(resourceItem.getTaxInclBasePrice());
        vo.setTaxExclCompilePrice(resourceItem.getTaxExclCompilePrice());
        vo.setTaxInclCompilePrice(resourceItem.getTaxInclCompilePrice());
        return vo;
    }
}
