package com.aegis.resource.material.service.impl;

import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.SecurityUtils;
import com.aegis.common.utils.StringUtils;
import com.aegis.resource.material.domain.Material;
import com.aegis.resource.material.domain.MaterialCategory;
import com.aegis.resource.material.domain.MaterialInventory;
import com.aegis.resource.material.mapper.ResourceMaterialCategoryMapper;
import com.aegis.resource.material.mapper.ResourceMaterialInventoryMapper;
import com.aegis.resource.material.mapper.ResourceMaterialMapper;
import com.aegis.resource.material.service.IResourceMaterialService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 物资Service业务层处理（MyBatis-Plus风格）
 *
 * @author aegis
 */
@Service
public class ResourceMaterialServiceImpl extends ServiceImpl<ResourceMaterialMapper, Material>
    implements IResourceMaterialService {

    @Autowired
    private ResourceMaterialCategoryMapper categoryMapper;

    @Autowired
    private ResourceMaterialInventoryMapper inventoryMapper;

    @Override
    public List<Material> listByCondition(Material condition) {
        LambdaQueryWrapper<Material> wrapper = new LambdaQueryWrapper<>();
        if (condition == null) {
            wrapper.orderByDesc(Material::getCreateTime);
        } else {
            wrapper.like(StringUtils.isNotBlank(condition.getMaterialName()), Material::getMaterialName, condition.getMaterialName());
            wrapper.like(StringUtils.isNotBlank(condition.getMaterialCode()), Material::getMaterialCode, condition.getMaterialCode());
            wrapper.eq(condition.getReusable() != null, Material::getReusable, condition.getReusable());
            wrapper.eq(condition.getSterilizeRequired() != null, Material::getSterilizeRequired, condition.getSterilizeRequired());
            if (condition.getCategoryId() != null) {
                List<Long> categoryIds = collectCategoryIds(condition.getCategoryId());
                if (!categoryIds.isEmpty()) {
                    wrapper.in(Material::getCategoryId, categoryIds);
                }
            }
            Object beginTime = condition.getParams().get("beginTime");
            if (beginTime != null) {
                wrapper.ge(Material::getCreateTime, beginTime);
            }
            Object endTime = condition.getParams().get("endTime");
            if (endTime != null) {
                wrapper.le(Material::getCreateTime, endTime);
            }
            wrapper.orderByDesc(Material::getCreateTime);
        }
        List<Material> materials = list(wrapper);
        fillDerivedFields(materials);
        return materials;
    }

    @Override
    public Material getById(Serializable id) {
        Material material = super.getById(id);
        if (material == null) {
            return null;
        }
        fillDerivedFields(Collections.singletonList(material));
        return material;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(Material material) {
        if (material == null) {
            throw new ServiceException("物资信息不能为空");
        }
        ensureMaterialCodeAvailable(material.getMaterialCode(), null, material.getMaterialName());
        applyDefaultValues(material);
        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        populateAuditOnCreate(material, operator, now);
        return super.save(material);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(Material material) {
        if (material == null || material.getId() == null) {
            throw new ServiceException("物资ID不能为空");
        }
        Material existing = super.getById(material.getId());
        if (existing == null) {
            throw new ServiceException("物资不存在");
        }
        if (StringUtils.isNotEmpty(material.getMaterialCode())
            && !StringUtils.equals(existing.getMaterialCode(), material.getMaterialCode())) {
            ensureMaterialCodeAvailable(material.getMaterialCode(), material.getId(), material.getMaterialName());
            existing.setMaterialCode(material.getMaterialCode());
        }
        mergeMaterial(existing, material);
        existing.setUpdateBy(SecurityUtils.getUsername());
        existing.setUpdateTime(new Date());
        return super.updateById(existing);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Serializable id) {
        if (id == null) {
            throw new ServiceException("物资ID不能为空");
        }
        Material material = super.getById(id);
        if (material == null) {
            throw new ServiceException("物资不存在");
        }
        // 物理删除：直接从数据库删除记录
        return super.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIds(Collection<?> idList) {
        if (idList == null || idList.isEmpty()) {
            return false;
        }
        // 批量查询验证所有ID对应的记录是否存在
        List<Long> ids = idList.stream()
            .filter(id -> id != null)
            .map(id -> (Long) id)
            .collect(Collectors.toList());
        if (ids.isEmpty()) {
            return false;
        }
        List<Material> materials = super.listByIds(ids);
        if (materials.size() != ids.size()) {
            Set<Long> foundIds = materials.stream()
                .map(Material::getId)
                .collect(Collectors.toSet());
            List<Long> missingIds = ids.stream()
                .filter(id -> !foundIds.contains(id))
                .collect(Collectors.toList());
            throw new ServiceException("物资不存在，ID: " + missingIds);
        }
        // 物理删除：直接从数据库删除记录
        return super.removeByIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateMaterialThreshold(Long id, BigDecimal threshold) {
        Material existing = super.getById(id);
        if (existing == null) {
            throw new ServiceException("物资不存在");
        }
        BigDecimal safeThreshold = threshold == null ? BigDecimal.ZERO : threshold;
        if (safeThreshold.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("报警阈值不能为负数");
        }
        existing.setThreshold(safeThreshold);
        existing.setUpdateBy(SecurityUtils.getUsername());
        existing.setUpdateTime(new Date());
        return super.updateById(existing);
    }

    private List<Long> collectCategoryIds(Long categoryId) {
        List<Long> ids = new ArrayList<>();
        ids.add(categoryId);
        LambdaQueryWrapper<MaterialCategory> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("find_in_set({0}, ancestors)", categoryId);
        List<MaterialCategory> children = categoryMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(children)) {
            ids.addAll(children.stream().map(MaterialCategory::getId).collect(Collectors.toList()));
        }
        return ids;
    }

    private void fillDerivedFields(List<Material> materials) {
        if (CollectionUtils.isEmpty(materials)) {
            return;
        }
        Set<Long> categoryIds = materials.stream()
            .map(Material::getCategoryId)
            .filter(id -> id != null && id > 0)
            .collect(Collectors.toSet());
        Map<Long, String> categoryNameMap = fetchCategoryNameMap(categoryIds);

        List<Long> materialIds = materials.stream()
            .map(Material::getId)
            .collect(Collectors.toList());
        Map<Long, MaterialInventory> inventoryMap = fetchInventorySummary(materialIds);

        for (Material material : materials) {
            material.setCategoryName(categoryNameMap.get(material.getCategoryId()));
            MaterialInventory summary = inventoryMap.get(material.getId());
            if (summary != null) {
                material.setTotalQty(summary.getTotalQty());
                material.setAvailableQty(summary.getAvailableQty());
            }
        }
    }

    private Map<Long, String> fetchCategoryNameMap(Set<Long> categoryIds) {
        if (CollectionUtils.isEmpty(categoryIds)) {
            return Collections.emptyMap();
        }
        List<MaterialCategory> categories = categoryMapper.selectBatchIds(new ArrayList<>(categoryIds));
        return categories.stream()
            .filter(category -> category != null)
            .collect(Collectors.toMap(MaterialCategory::getId, MaterialCategory::getCategoryName));
    }

    private Map<Long, MaterialInventory> fetchInventorySummary(List<Long> materialIds) {
        if (CollectionUtils.isEmpty(materialIds)) {
            return Collections.emptyMap();
        }
        LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(MaterialInventory::getMaterialId, materialIds);
        List<MaterialInventory> inventories = inventoryMapper.selectList(wrapper);
        
        // 按materialId分组并聚合
        Map<Long, MaterialInventory> result = new HashMap<>();
        Map<Long, List<MaterialInventory>> grouped = inventories.stream()
            .collect(Collectors.groupingBy(MaterialInventory::getMaterialId));
        
        for (Map.Entry<Long, List<MaterialInventory>> entry : grouped.entrySet()) {
            MaterialInventory summary = new MaterialInventory();
            summary.setMaterialId(entry.getKey());
            BigDecimal totalQty = entry.getValue().stream()
                .map(MaterialInventory::getTotalQty)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal availableQty = entry.getValue().stream()
                .map(MaterialInventory::getAvailableQty)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            summary.setTotalQty(totalQty);
            summary.setAvailableQty(availableQty);
            result.put(entry.getKey(), summary);
        }
        return result;
    }

    private void ensureMaterialCodeAvailable(String materialCode, Long currentId, String materialName) {
        if (StringUtils.isEmpty(materialCode)) {
            throw new ServiceException("物资编码不能为空");
        }
        LambdaQueryWrapper<Material> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Material::getMaterialCode, materialCode);
        if (currentId != null) {
            wrapper.ne(Material::getId, currentId);
        }
        Material exists = getOne(wrapper);
        if (exists != null) {
            String name = StringUtils.defaultString(materialName, materialCode);
            throw new ServiceException("物资编码已存在：" + name);
        }
    }

    private void mergeMaterial(Material target, Material source) {
        if (StringUtils.isNotEmpty(source.getMaterialName())) {
            target.setMaterialName(source.getMaterialName());
        }
        if (source.getCategoryId() != null) {
            target.setCategoryId(source.getCategoryId());
        }
        if (StringUtils.isNotEmpty(source.getUnit())) {
            target.setUnit(source.getUnit());
        }
        if (StringUtils.isNotEmpty(source.getSpec())) {
            target.setSpec(source.getSpec());
        }
        if (StringUtils.isNotEmpty(source.getBrand())) {
            target.setBrand(source.getBrand());
        }
        if (StringUtils.isNotEmpty(source.getBatchNo())) {
            target.setBatchNo(source.getBatchNo());
        }
        if (source.getExpireDate() != null) {
            target.setExpireDate(source.getExpireDate());
        }
        if (source.getReusable() != null) {
            target.setReusable(source.getReusable());
        }
        if (StringUtils.isNotEmpty(source.getRecyclePolicy())) {
            target.setRecyclePolicy(source.getRecyclePolicy());
        }
        if (source.getSterilizeRequired() != null) {
            target.setSterilizeRequired(source.getSterilizeRequired());
        }
        if (source.getThreshold() != null) {
            if (source.getThreshold().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("报警阈值不能为负数");
            }
            target.setThreshold(source.getThreshold());
        }
        if (StringUtils.isNotEmpty(source.getRemark())) {
            target.setRemark(source.getRemark());
        }
    }

    private void applyDefaultValues(Material material) {
        if (material.getReusable() == null) {
            material.setReusable(0);
        }
        if (material.getSterilizeRequired() == null) {
            material.setSterilizeRequired(0);
        }
        if (material.getThreshold() == null) {
            material.setThreshold(BigDecimal.ZERO);
        }
        if (material.getLockedQty() == null) {
            material.setLockedQty(BigDecimal.ZERO);
        }
        if (material.getUsedQty() == null) {
            material.setUsedQty(BigDecimal.ZERO);
        }
    }

    private void populateAuditOnCreate(Material material, String operator, Date now) {
        material.setCreateBy(operator);
        material.setCreateTime(now);
        material.setUpdateBy(operator);
        material.setUpdateTime(now);
    }
}