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.QuotaMarketMaterialRespVO;
import com.yhy.module.core.controller.admin.quota.vo.QuotaMarketMaterialSaveReqVO;
import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO;
import com.yhy.module.core.dal.dataobject.quota.QuotaMarketMaterialDO;
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.QuotaMarketMaterialMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceCatalogItemMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper;
import com.yhy.module.core.dal.mysql.resource.ResourceMergedMapper;
import com.yhy.module.core.enums.ErrorCodeConstants;
import com.yhy.module.core.service.quota.QuotaItemService;
import com.yhy.module.core.service.quota.QuotaMarketMaterialService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
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 QuotaMarketMaterialServiceImpl implements QuotaMarketMaterialService {

    @Resource
    private QuotaMarketMaterialMapper quotaMarketMaterialMapper;

    @Resource
    private ResourceItemMapper resourceItemMapper;

    @Resource
    private ResourceCatalogItemMapper resourceCatalogItemMapper;

    @Resource
    private ResourceMergedMapper resourceMergedMapper;

    @Resource
    @Lazy
    private QuotaItemService quotaItemService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createMarketMaterial(QuotaMarketMaterialSaveReqVO 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. 定额消耗量默认为0
        if (createReqVO.getDosage() == null) {
            createReqVO.setDosage(java.math.BigDecimal.ZERO);
        }

        // 4. 转换为 DO
        QuotaMarketMaterialDO marketMaterial = convertToDO(createReqVO);
        
        // 4. 处理 sortOrder
        if (marketMaterial.getSortOrder() == null) {
            Integer maxSortOrder = getMaxSortOrder(createReqVO.getQuotaItemId());
            marketMaterial.setSortOrder(maxSortOrder + 1);
        } else {
            shiftSortOrdersFrom(createReqVO.getQuotaItemId(), marketMaterial.getSortOrder());
        }
        
        // 5. 插入
        quotaMarketMaterialMapper.insert(marketMaterial);
        
        log.info("[createMarketMaterial] 添加定额市场主材设备成功，quotaItemId={}, resourceItemId={}", 
            createReqVO.getQuotaItemId(), createReqVO.getResourceItemId());
        
        return marketMaterial.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMarketMaterial(QuotaMarketMaterialSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());
        
        // 验证工料机是否在范围内（如果修改了 resourceItemId）
        QuotaMarketMaterialDO existing = quotaMarketMaterialMapper.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);
            }
        }
        
        // 校验：单位为%的工料机不允许修改定额消耗量
        ResourceItemDO resourceItem = resourceItemMapper.selectById(existing.getResourceItemId());
        if (resourceItem != null && "%".equals(resourceItem.getUnit())) {
            if (updateReqVO.getDosage() != null && existing.getDosage() != null 
                    && updateReqVO.getDosage().compareTo(existing.getDosage()) != 0) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_RESOURCE_PERCENT_UNIT_DOSAGE_READONLY);
            }
        }
        
        // 转换为 DO
        QuotaMarketMaterialDO 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);
        }
        
        quotaMarketMaterialMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMarketMaterial(Long id) {
        validateExists(id);
        quotaMarketMaterialMapper.deleteById(id);
    }

    @Override
    public QuotaMarketMaterialDO getMarketMaterial(Long id) {
        return quotaMarketMaterialMapper.selectById(id);
    }

    @Override
    public List<QuotaMarketMaterialRespVO> getMarketMaterialList(Long quotaItemId) {
        List<QuotaMarketMaterialDO> list = quotaMarketMaterialMapper.selectListByQuotaItemId(quotaItemId);
        List<QuotaMarketMaterialRespVO> result = list.stream().map(this::convertToVO).collect(Collectors.toList());
        
        // 格式化数值
        result.forEach(this::formatVO);
        
        return result;
    }

    @Override
    public List<ResourceItemRespVO> getAvailableResourceItems(Long quotaItemId) {
        Long categoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        List<ResourceItemDO> resourceItems = resourceItemMapper.selectByCategoryTreeId(categoryTreeId);
        return resourceItems.stream()
            .map(this::convertResourceItemToVO)
            .collect(Collectors.toList());
    }

    @Override
    public List<ResourceItemRespVO> getAvailableResourceItemsWithFilter(Long quotaItemId, String code, String name, String spec) {
        Long categoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        List<ResourceItemDO> resourceItems = resourceItemMapper.selectByCategoryTreeIdWithFilter(
            categoryTreeId, code, name, spec);
        return resourceItems.stream()
            .map(this::convertResourceItemToVO)
            .collect(Collectors.toList());
    }

    @Override
    public ResourceItemRespVO getResourceItemByCode(Long quotaItemId, String code) {
        Long categoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        ResourceItemDO resourceItem = resourceItemMapper.selectByCategoryTreeIdAndCode(categoryTreeId, code);
        if (resourceItem == null) {
            return null;
        }
        return convertResourceItemToVO(resourceItem);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchCreateMarketMaterials(Long quotaItemId, List<QuotaMarketMaterialSaveReqVO> materials) {
        for (QuotaMarketMaterialSaveReqVO material : materials) {
            material.setQuotaItemId(quotaItemId);
            createMarketMaterial(material);
        }
    }

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

    private void validateResourceInScope(Long quotaItemId, Long resourceItemId) {
        Long allowedCategoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId);
        
        ResourceItemDO resourceItem = resourceItemMapper.selectById(resourceItemId);
        if (resourceItem == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS);
        }
        
        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);
        }
        
        if (!allowedCategoryTreeId.equals(catalogItem.getCategoryTreeId())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_RESOURCE_OUT_OF_SCOPE);
        }
    }

    private QuotaMarketMaterialDO validateExists(Long id) {
        QuotaMarketMaterialDO marketMaterial = quotaMarketMaterialMapper.selectById(id);
        if (marketMaterial == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_MARKET_MATERIAL_NOT_EXISTS);
        }
        return marketMaterial;
    }

    private QuotaMarketMaterialDO convertToDO(QuotaMarketMaterialSaveReqVO vo) {
        QuotaMarketMaterialDO marketMaterial = new QuotaMarketMaterialDO();
        marketMaterial.setQuotaItemId(vo.getQuotaItemId());
        marketMaterial.setResourceItemId(vo.getResourceItemId());
        marketMaterial.setDosage(vo.getDosage());
        marketMaterial.setAdjustedDosage(vo.getAdjustedDosage());
        marketMaterial.setAttributes(vo.getAttributes());
        marketMaterial.setSortOrder(vo.getSortOrder());
        return marketMaterial;
    }

    private Integer getMaxSortOrder(Long quotaItemId) {
        List<QuotaMarketMaterialDO> materials = quotaMarketMaterialMapper.selectListByQuotaItemId(quotaItemId);
        if (materials == null || materials.isEmpty()) {
            return 0;
        }
        return materials.stream()
                .map(QuotaMarketMaterialDO::getSortOrder)
                .filter(java.util.Objects::nonNull)
                .max(Integer::compareTo)
                .orElse(0);
    }

    private void shiftSortOrdersFrom(Long quotaItemId, Integer fromSortOrder) {
        List<QuotaMarketMaterialDO> materials = quotaMarketMaterialMapper.selectListByQuotaItemId(quotaItemId);
        for (QuotaMarketMaterialDO material : materials) {
            if (material.getSortOrder() != null && material.getSortOrder() >= fromSortOrder) {
                material.setSortOrder(material.getSortOrder() + 1);
                quotaMarketMaterialMapper.updateById(material);
            }
        }
    }

    private QuotaMarketMaterialRespVO convertToVO(QuotaMarketMaterialDO marketMaterial) {
        QuotaMarketMaterialRespVO vo = new QuotaMarketMaterialRespVO();
        vo.setId(marketMaterial.getId());
        vo.setQuotaItemId(marketMaterial.getQuotaItemId());
        vo.setResourceItemId(marketMaterial.getResourceItemId());
        vo.setDosage(marketMaterial.getDosage());
        vo.setAdjustedDosage(marketMaterial.getAdjustedDosage());
        vo.setAttributes(marketMaterial.getAttributes());
        vo.setCreateTime(marketMaterial.getCreateTime());
        vo.setUpdateTime(marketMaterial.getUpdateTime());
        
        // 扩展字段
        vo.setLossRate(marketMaterial.getLossRate());
        
        // 实时查询工料机信息
        ResourceItemDO resourceItem = resourceItemMapper.selectById(marketMaterial.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(convertTypeToDisplay(resourceItem.getType()));
            vo.setCalcBase(resourceItem.getCalcBase());
            
            boolean isMerged = resourceItem.getIsMerged() != null && resourceItem.getIsMerged() == 1;
            vo.setIsMerged(isMerged);
            
            if (isMerged) {
                vo.setResourceTaxRate(null);
                vo.setResourceTaxExclBasePrice(null);
                vo.setResourceTaxInclBasePrice(null);
                vo.setResourceTaxExclCompilePrice(null);
                vo.setResourceTaxInclCompilePrice(null);
                vo.setPrice(null);
                
                // 查询复合工料机子数据
                List<ResourceMergedDO> mergedList = resourceMergedMapper.selectByMergedId(resourceItem.getId());
                if (mergedList != null && !mergedList.isEmpty()) {
                    List<QuotaMarketMaterialRespVO.MergedResourceItemVO> mergedItems = new ArrayList<>();
                    for (ResourceMergedDO merged : mergedList) {
                        QuotaMarketMaterialRespVO.MergedResourceItemVO item = convertMergedToVO(merged, marketMaterial.getDosage());
                        if (item != null) {
                            mergedItems.add(item);
                        }
                    }
                    vo.setMergedItems(mergedItems);
                    
                    // 计算复合工料机合价
                    calculateMergedTotalSums(vo, mergedItems);
                }
            } else {
                vo.setResourceTaxRate(resourceItem.getTaxRate());
                vo.setResourceTaxExclBasePrice(resourceItem.getTaxExclBasePrice());
                vo.setResourceTaxInclBasePrice(resourceItem.getTaxInclBasePrice());
                vo.setResourceTaxExclCompilePrice(resourceItem.getTaxExclCompilePrice());
                vo.setResourceTaxInclCompilePrice(resourceItem.getTaxInclCompilePrice());
                
                if (resourceItem.getTaxExclBasePrice() != null) {
                    vo.setPrice(resourceItem.getTaxExclBasePrice());
                } else if (resourceItem.getTaxInclBasePrice() != null) {
                    vo.setPrice(resourceItem.getTaxInclBasePrice());
                }
                
                // 计算合价
                BigDecimal effectiveDosage = marketMaterial.getDosage();
                if (effectiveDosage != null) {
                    vo.setTaxExclBaseTotalSum(multiplyOrZero(resourceItem.getTaxExclBasePrice(), effectiveDosage));
                    vo.setTaxInclBaseTotalSum(multiplyOrZero(resourceItem.getTaxInclBasePrice(), effectiveDosage));
                    vo.setTaxExclCompileTotalSum(multiplyOrZero(resourceItem.getTaxExclCompilePrice(), effectiveDosage));
                    vo.setTaxInclCompileTotalSum(multiplyOrZero(resourceItem.getTaxInclCompilePrice(), effectiveDosage));
                }
            }
            
            // 计算实际消耗量和金额
            vo.setActualDosage(marketMaterial.getActualDosage());
            if (vo.getPrice() != null && vo.getActualDosage() != null) {
                vo.setAmount(vo.getActualDosage().multiply(vo.getPrice()));
            }
        }
        
        return vo;
    }

    private QuotaMarketMaterialRespVO.MergedResourceItemVO convertMergedToVO(ResourceMergedDO merged, BigDecimal parentDosage) {
        if (merged.getSourceId() == null) {
            return null;
        }
        
        ResourceItemDO sourceItem = resourceItemMapper.selectById(merged.getSourceId());
        if (sourceItem == null) {
            return null;
        }
        
        QuotaMarketMaterialRespVO.MergedResourceItemVO item = new QuotaMarketMaterialRespVO.MergedResourceItemVO();
        item.setId(merged.getId());
        item.setResourceItemId(merged.getSourceId());
        item.setResourceCode(sourceItem.getCode());
        item.setResourceName(sourceItem.getName());
        item.setResourceUnit(sourceItem.getUnit());
        item.setResourceSpec(sourceItem.getSpec());
        item.setResourceCategoryId(sourceItem.getCategoryId());
        item.setResourceType(convertTypeToDisplay(sourceItem.getType()));
        item.setResourceTaxRate(sourceItem.getTaxRate());
        item.setResourceTaxExclBasePrice(sourceItem.getTaxExclBasePrice());
        item.setResourceTaxInclBasePrice(sourceItem.getTaxInclBasePrice());
        item.setResourceTaxExclCompilePrice(sourceItem.getTaxExclCompilePrice());
        item.setResourceTaxInclCompilePrice(sourceItem.getTaxInclCompilePrice());
        item.setCalcBase(sourceItem.getCalcBase());
        
        // 子项定额消耗量 = 原定额消耗量 × 父定额消耗量
        BigDecimal childDosage = merged.getQuotaConsumption();
        if (childDosage != null && parentDosage != null) {
            item.setDosage(childDosage.multiply(parentDosage).setScale(6, RoundingMode.HALF_UP));
        } else {
            item.setDosage(childDosage);
        }
        item.setPrice(merged.getTaxExclMarketPrice());
        
        // 计算子项合价
        BigDecimal factor = item.getDosage();
        if (factor != null) {
            item.setTaxExclBaseTotalSum(multiplyOrZero(sourceItem.getTaxExclBasePrice(), factor));
            item.setTaxInclBaseTotalSum(multiplyOrZero(sourceItem.getTaxInclBasePrice(), factor));
            item.setTaxExclCompileTotalSum(multiplyOrZero(sourceItem.getTaxExclCompilePrice(), factor));
            item.setTaxInclCompileTotalSum(multiplyOrZero(sourceItem.getTaxInclCompilePrice(), factor));
        }
        
        return item;
    }

    private void calculateMergedTotalSums(QuotaMarketMaterialRespVO vo, List<QuotaMarketMaterialRespVO.MergedResourceItemVO> mergedItems) {
        BigDecimal taxExclBaseSum = BigDecimal.ZERO;
        BigDecimal taxInclBaseSum = BigDecimal.ZERO;
        BigDecimal taxExclCompileSum = BigDecimal.ZERO;
        BigDecimal taxInclCompileSum = BigDecimal.ZERO;
        
        for (QuotaMarketMaterialRespVO.MergedResourceItemVO item : mergedItems) {
            if (item.getTaxExclBaseTotalSum() != null) {
                taxExclBaseSum = taxExclBaseSum.add(item.getTaxExclBaseTotalSum());
            }
            if (item.getTaxInclBaseTotalSum() != null) {
                taxInclBaseSum = taxInclBaseSum.add(item.getTaxInclBaseTotalSum());
            }
            if (item.getTaxExclCompileTotalSum() != null) {
                taxExclCompileSum = taxExclCompileSum.add(item.getTaxExclCompileTotalSum());
            }
            if (item.getTaxInclCompileTotalSum() != null) {
                taxInclCompileSum = taxInclCompileSum.add(item.getTaxInclCompileTotalSum());
            }
        }
        
        vo.setTaxExclBaseTotalSum(taxExclBaseSum);
        vo.setTaxInclBaseTotalSum(taxInclBaseSum);
        vo.setTaxExclCompileTotalSum(taxExclCompileSum);
        vo.setTaxInclCompileTotalSum(taxInclCompileSum);
    }

    private void formatVO(QuotaMarketMaterialRespVO vo) {
        // 价格/税率/合价：2位小数
        vo.setResourceTaxRate(roundToScale(vo.getResourceTaxRate(), 2));
        vo.setResourceTaxExclBasePrice(roundToScale(vo.getResourceTaxExclBasePrice(), 2));
        vo.setResourceTaxInclBasePrice(roundToScale(vo.getResourceTaxInclBasePrice(), 2));
        vo.setResourceTaxExclCompilePrice(roundToScale(vo.getResourceTaxExclCompilePrice(), 2));
        vo.setResourceTaxInclCompilePrice(roundToScale(vo.getResourceTaxInclCompilePrice(), 2));
        vo.setTaxExclBaseTotalSum(roundToScale(vo.getTaxExclBaseTotalSum(), 2));
        vo.setTaxInclBaseTotalSum(roundToScale(vo.getTaxInclBaseTotalSum(), 2));
        vo.setTaxExclCompileTotalSum(roundToScale(vo.getTaxExclCompileTotalSum(), 2));
        vo.setTaxInclCompileTotalSum(roundToScale(vo.getTaxInclCompileTotalSum(), 2));
        vo.setPrice(roundToScale(vo.getPrice(), 2));
        vo.setAmount(roundToScale(vo.getAmount(), 2));
        
        // 消耗量：4位小数
        vo.setDosage(roundToScale(vo.getDosage(), 4));
        vo.setAdjustedDosage(roundToScale(vo.getAdjustedDosage(), 4));
        vo.setActualDosage(roundToScale(vo.getActualDosage(), 4));
        
        // 格式化子工料机
        if (vo.getMergedItems() != null) {
            for (QuotaMarketMaterialRespVO.MergedResourceItemVO item : vo.getMergedItems()) {
                item.setResourceTaxRate(roundToScale(item.getResourceTaxRate(), 2));
                item.setResourceTaxExclBasePrice(roundToScale(item.getResourceTaxExclBasePrice(), 2));
                item.setResourceTaxInclBasePrice(roundToScale(item.getResourceTaxInclBasePrice(), 2));
                item.setResourceTaxExclCompilePrice(roundToScale(item.getResourceTaxExclCompilePrice(), 2));
                item.setResourceTaxInclCompilePrice(roundToScale(item.getResourceTaxInclCompilePrice(), 2));
                item.setTaxExclBaseTotalSum(roundToScale(item.getTaxExclBaseTotalSum(), 2));
                item.setTaxInclBaseTotalSum(roundToScale(item.getTaxInclBaseTotalSum(), 2));
                item.setTaxExclCompileTotalSum(roundToScale(item.getTaxExclCompileTotalSum(), 2));
                item.setTaxInclCompileTotalSum(roundToScale(item.getTaxInclCompileTotalSum(), 2));
                item.setPrice(roundToScale(item.getPrice(), 2));
                item.setAmount(roundToScale(item.getAmount(), 2));
                item.setDosage(roundToScale(item.getDosage(), 4));
                item.setActualDosage(roundToScale(item.getActualDosage(), 4));
            }
        }
    }

    private BigDecimal roundToScale(BigDecimal value, int scale) {
        if (value == null) {
            return null;
        }
        return value.setScale(scale, RoundingMode.HALF_UP);
    }

    private BigDecimal multiplyOrZero(BigDecimal a, BigDecimal b) {
        if (a == null || b == null) {
            return BigDecimal.ZERO;
        }
        return a.multiply(b);
    }

    private String convertTypeToDisplay(String type) {
        if (type == null) {
            return null;
        }
        switch (type) {
            case "1":
                return "labor";
            case "2":
                return "material";
            case "3":
                return "machine";
            default:
                return type;
        }
    }

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