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.ResourceCatalogItemDO;
import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 定额工料机组成 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;

    @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. 快照工料机信息
        snapshotResourceInfo(quotaResource, resourceItem);
        
        // 5. 插入
        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());
        }
        
        // 转换为 DO
        QuotaResourceDO updateObj = convertToDO(updateReqVO);
        updateObj.setId(updateReqVO.getId());
        
        // 如果修改了工料机，重新快照
        if (!existing.getResourceItemId().equals(updateReqVO.getResourceItemId())) {
            ResourceItemDO resourceItem = resourceItemMapper.selectById(updateReqVO.getResourceItemId());
            snapshotResourceInfo(updateObj, resourceItem);
        }
        
        // 更新
        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 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. 获取工料机所属的目录节点
        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 void snapshotResourceInfo(QuotaResourceDO quotaResource, ResourceItemDO resourceItem) {
        // 快照基本信息
        quotaResource.setResourceCode(resourceItem.getName()); // 使用 name 作为 code
        quotaResource.setResourceName(resourceItem.getName());
        quotaResource.setResourceUnit(resourceItem.getUnit());
        
        // 快照价格（优先使用除税基价）
        if (resourceItem.getTaxExclBasePrice() != null) {
            quotaResource.setPrice(resourceItem.getTaxExclBasePrice());
        } else if (resourceItem.getTaxInclBasePrice() != null) {
            quotaResource.setPrice(resourceItem.getTaxInclBasePrice());
        }
        
        // 快照资源类型
        if (quotaResource.getAttributes() == null) {
            quotaResource.setAttributes(new java.util.HashMap<>());
        }
        quotaResource.getAttributes().put("resource_type", resourceItem.getType());
        
        log.debug("[snapshotResourceInfo] 快照工料机信息，resourceItemId={}, name={}, unit={}, price={}", 
            resourceItem.getId(), resourceItem.getName(), resourceItem.getUnit(), quotaResource.getPrice());
    }

    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());
        vo.setPrice(quotaResource.getPrice());
        vo.setResourceCode(quotaResource.getResourceCode());
        vo.setResourceName(quotaResource.getResourceName());
        vo.setResourceUnit(quotaResource.getResourceUnit());
        vo.setActualDosage(quotaResource.getActualDosage());
        vo.setAmount(quotaResource.getAmount());
        
        return vo;
    }

    private ResourceItemRespVO convertResourceItemToVO(ResourceItemDO resourceItem) {
        ResourceItemRespVO vo = new ResourceItemRespVO();
        vo.setId(resourceItem.getId());
        vo.setCatalogItemId(resourceItem.getCatalogItemId());
        vo.setName(resourceItem.getName());
        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());
        return vo;
    }
}
