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

import java.math.BigDecimal;
import java.util.ArrayList;
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.util.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.SecurityUtils;
import com.aegis.resource.material.domain.MaterialInventory;
import com.aegis.resource.material.dto.InventoryAction;
import com.aegis.resource.material.dto.InventoryAdjustment;
import com.aegis.resource.material.domain.MaterialUsage;
import com.aegis.resource.material.mapper.ResourceMaterialInventoryMapper;
import com.aegis.resource.material.service.IResourceMaterialInventoryService;
import com.aegis.resource.material.service.IResourceMaterialUsageService;
import com.aegis.resource.warehouse.domain.Warehouse;
import com.aegis.resource.warehouse.mapper.ResourceWarehouseMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 物资库存Service业务层处理（MyBatis-Plus风格）
 * 
 * @author aegis
 */
@Service
public class ResourceMaterialInventoryServiceImpl extends ServiceImpl<ResourceMaterialInventoryMapper, MaterialInventory>
    implements IResourceMaterialInventoryService {

    @Autowired
    private ResourceWarehouseMapper warehouseMapper;

    @Autowired
    private IResourceMaterialUsageService usageService;

    /**
     * 查询物资库存列表
     * 
     * @param inventory 库存信息
     * @return 库存集合
     */
    @Override
    public List<MaterialInventory> listByCondition(MaterialInventory inventory) {
        // 构建查询条件
        LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
        if (inventory.getMaterialId() != null) {
            wrapper.eq(MaterialInventory::getMaterialId, inventory.getMaterialId());
        }
        if (inventory.getWarehouseId() != null) {
            wrapper.eq(MaterialInventory::getWarehouseId, inventory.getWarehouseId());
        }
        wrapper.orderByDesc(MaterialInventory::getCreateTime);

        // 执行查询
        List<MaterialInventory> inventories = super.list(wrapper);
        
        // 批量填充仓库名称（避免N+1查询问题）
        this.fillWarehouseNames(inventories);
        return inventories;
    }

    /**
     * 根据物资ID查询库存列表
     * 
     * @param materialId 物资ID
     * @return 库存集合
     */
    @Override
    public List<MaterialInventory> listByMaterialId(Long materialId) {
        LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MaterialInventory::getMaterialId, materialId).orderByDesc(MaterialInventory::getCreateTime);
        List<MaterialInventory> inventories = super.list(wrapper);
        // 批量填充仓库名称（避免N+1查询问题）
        this.fillWarehouseNames(inventories);
        return inventories;
    }

    /**
     * 添加库存（指定仓库和数量）
     * 
     * @param inventory 库存信息
     * @return 结果
     */
    @Override
    @Transactional
    public boolean addInventory(MaterialInventory inventory) {
        // 校验必填字段
        if (inventory.getMaterialId() == null) {
            throw new ServiceException("物资ID不能为空");
        }
        if (inventory.getWarehouseId() == null) {
            throw new ServiceException("仓库ID不能为空");
        }
        // 校验仓库是否存在
        Warehouse warehouse = warehouseMapper.selectById(inventory.getWarehouseId());
        if (warehouse == null) {
            throw new ServiceException("仓库不存在");
        }
        // 检查是否已存在该物资在该仓库的库存记录
        LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MaterialInventory::getMaterialId, inventory.getMaterialId())
            .eq(MaterialInventory::getWarehouseId, inventory.getWarehouseId())
            .last("limit 1");
        MaterialInventory existing = getOne(wrapper);
        if (existing != null) {
            // 验证查询到的记录确实是同一个仓库（防止查询条件错误）
            if (!existing.getWarehouseId().equals(inventory.getWarehouseId())) {
                throw new ServiceException("查询到的库存记录仓库ID不匹配，可能存在数据异常");
            }
            // 更新现有库存（使用乐观锁重试机制）
            int maxRetries = 3;
            int retryCount = 0;
            while (retryCount < maxRetries) {
                // 重新查询获取最新版本
                LambdaQueryWrapper<MaterialInventory> latestWrapper = new LambdaQueryWrapper<>();
                latestWrapper.eq(MaterialInventory::getMaterialId, inventory.getMaterialId())
                    .eq(MaterialInventory::getWarehouseId, inventory.getWarehouseId())
                    .last("limit 1");
                MaterialInventory latest = getOne(latestWrapper);
                if (latest == null) {
                    throw new ServiceException("库存记录不存在");
                }
                // 再次验证仓库ID匹配
                if (!latest.getWarehouseId().equals(inventory.getWarehouseId())) {
                    throw new ServiceException("查询到的库存记录仓库ID不匹配，可能存在数据异常");
                }
                BigDecimal newTotalQty = latest.getTotalQty().add(inventory.getTotalQty());
                latest.setTotalQty(newTotalQty);
                latest.setAvailableQty(this.calculateAvailableQty(latest));
                latest.setUpdateBy(SecurityUtils.getUsername());
                boolean result = updateById(latest);
                if (result) {
                    return true;
                }
                retryCount++;
                if (retryCount >= maxRetries) {
                    throw new ServiceException("库存操作冲突，请稍后重试");
                }
                try {
                    Thread.sleep(50 * retryCount);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new ServiceException("库存操作被中断");
                }
            }
            throw new ServiceException("库存操作失败，请稍后重试");
        } else {
            // 新增库存记录
            inventory.setCreateBy(SecurityUtils.getUsername());
            if (inventory.getTotalQty() == null) {
                inventory.setTotalQty(BigDecimal.ZERO);
            }
            if (inventory.getLockedQty() == null) {
                inventory.setLockedQty(BigDecimal.ZERO);
            }
            if (inventory.getUsedQty() == null) {
                inventory.setUsedQty(BigDecimal.ZERO);
            }
            if (inventory.getVersion() == null) {
                inventory.setVersion(1); // 初始版本号
            }
            inventory.setAvailableQty(this.calculateAvailableQty(inventory));
            return save(inventory);
        }
    }

    /**
     * 占用库存（需指定仓库ID）
     * 
     * @param materialId 物资ID
     * @param warehouseId 仓库ID
     * @param qty 占用数量
     * @param eventId 事件ID（可选）
     * @param taskId 任务ID（可选）
     * @param remark 备注
     * @return 结果
     */
    @Override
    @Transactional
    public boolean lockInventory(InventoryAction action) {
        int maxRetries = 3;
        int retryCount = 0;
        
        while (retryCount < maxRetries) {
            // 重新查询获取最新版本
            LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MaterialInventory::getMaterialId, action.getMaterialId())
                .eq(MaterialInventory::getWarehouseId, action.getWarehouseId())
                .last("limit 1");
            MaterialInventory inventory = getOne(wrapper);
            if (inventory == null) {
                throw new ServiceException("该物资在该仓库没有库存");
            }
            // 检查可用数量是否充足
            if (inventory.getAvailableQty().compareTo(action.getQty()) < 0) {
                throw new ServiceException("可用库存不足，当前可用：" + inventory.getAvailableQty() + "，需要：" + action.getQty());
            }
            // 更新锁定数量
            inventory.setLockedQty(inventory.getLockedQty().add(action.getQty()));
            inventory.setAvailableQty(this.calculateAvailableQty(inventory));
            inventory.setUpdateBy(SecurityUtils.getUsername());
            boolean result = this.updateById(inventory);
            
            // 如果更新成功，说明乐观锁通过
            if (result) {
                // 记录使用记录
                this.recordUsage(action.getMaterialId(), action.getWarehouseId(), action.getQty(), "lock", action.getEventId(), action.getTaskId(), action.getRemark());
                return true;
            }
            
            // 更新失败，说明版本冲突，重试
            retryCount++;
            if (retryCount >= maxRetries) {
                throw new ServiceException("库存操作冲突，请稍后重试");
            }
            // 短暂等待后重试（避免立即重试导致数据库压力）
            try {
                Thread.sleep(50 * retryCount); // 递增等待时间：50ms, 100ms, 150ms
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ServiceException("库存操作被中断");
            }
        }
        throw new ServiceException("库存操作失败，请稍后重试");
    }

    /**
     * 释放库存（需指定仓库ID）
     * 
     * @param materialId 物资ID
     * @param warehouseId 仓库ID
     * @param qty 释放数量
     * @param eventId 事件ID（可选）
     * @param taskId 任务ID（可选）
     * @param remark 备注
     * @return 结果
     */
    @Override
    @Transactional
    public boolean unlockInventory(InventoryAction action) {
        int maxRetries = 3;
        int retryCount = 0;
        
        while (retryCount < maxRetries) {
            // 重新查询获取最新版本
            LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MaterialInventory::getMaterialId, action.getMaterialId())
                .eq(MaterialInventory::getWarehouseId, action.getWarehouseId())
                .last("limit 1");
            MaterialInventory inventory = getOne(wrapper);
            if (inventory == null) {
                throw new ServiceException("该物资在该仓库没有库存");
            }
            // 检查锁定数量是否充足
            if (inventory.getLockedQty().compareTo(action.getQty()) < 0) {
                throw new ServiceException("锁定库存不足，当前锁定：" + inventory.getLockedQty() + "，需要释放：" + action.getQty());
            }
            // 更新锁定数量
            inventory.setLockedQty(inventory.getLockedQty().subtract(action.getQty()));
            inventory.setAvailableQty(this.calculateAvailableQty(inventory));
            inventory.setUpdateBy(SecurityUtils.getUsername());
            boolean result = this.updateById(inventory);
            
            // 如果更新成功，说明乐观锁通过
            if (result) {
                // 记录使用记录
                this.recordUsage(action.getMaterialId(), action.getWarehouseId(), action.getQty(), "unlock", action.getEventId(), action.getTaskId(), action.getRemark());
                return true;
            }
            
            // 更新失败，说明版本冲突，重试
            retryCount++;
            if (retryCount >= maxRetries) {
                throw new ServiceException("库存操作冲突，请稍后重试");
            }
            // 短暂等待后重试
            try {
                Thread.sleep(50 * retryCount);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ServiceException("库存操作被中断");
            }
        }
        throw new ServiceException("库存操作失败，请稍后重试");
    }

    /**
     * 消耗结算（一次性物资，需指定仓库ID）
     * 
     * @param materialId 物资ID
     * @param warehouseId 仓库ID
     * @param qty 消耗数量
     * @param eventId 事件ID（可选）
     * @param taskId 任务ID（可选）
     * @param remark 备注
     * @return 结果
     */
    @Override
    @Transactional
    public boolean consumeInventory(InventoryAction action) {
        int maxRetries = 3;
        int retryCount = 0;
        
        while (retryCount < maxRetries) {
            // 重新查询获取最新版本
            LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MaterialInventory::getMaterialId, action.getMaterialId())
                .eq(MaterialInventory::getWarehouseId, action.getWarehouseId())
                .last("limit 1");
            MaterialInventory inventory = getOne(wrapper);
            if (inventory == null) {
                throw new ServiceException("该物资在该仓库没有库存");
            }
            // 检查锁定数量是否充足（消耗的应该是已锁定的库存）
            if (inventory.getLockedQty().compareTo(action.getQty()) < 0) {
                throw new ServiceException("锁定库存不足，当前锁定：" + inventory.getLockedQty() + "，需要消耗：" + action.getQty());
            }
            // 更新库存：减少锁定数量和总数量，增加已使用数量
            inventory.setLockedQty(inventory.getLockedQty().subtract(action.getQty()));
            inventory.setTotalQty(inventory.getTotalQty().subtract(action.getQty()));
            inventory.setUsedQty(inventory.getUsedQty().add(action.getQty()));
            inventory.setAvailableQty(calculateAvailableQty(inventory));
            inventory.setUpdateBy(SecurityUtils.getUsername());
            boolean result = this.updateById(inventory);
            
            // 如果更新成功，说明乐观锁通过
            if (result) {
                // 记录使用记录
                this.recordUsage(action.getMaterialId(), action.getWarehouseId(), action.getQty(), "consume", action.getEventId(), action.getTaskId(), action.getRemark());
                return true;
            }
            
            // 更新失败，说明版本冲突，重试
            retryCount++;
            if (retryCount >= maxRetries) {
                throw new ServiceException("库存操作冲突，请稍后重试");
            }
            // 短暂等待后重试
            try {
                Thread.sleep(50 * retryCount);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ServiceException("库存操作被中断");
            }
        }
        throw new ServiceException("库存操作失败，请稍后重试");
    }

    /**
     * 归还入库（可重复使用物资，需指定仓库ID）
     * 
     * @param materialId 物资ID
     * @param warehouseId 仓库ID
     * @param qty 归还数量
     * @param eventId 事件ID（可选）
     * @param taskId 任务ID（可选）
     * @param remark 备注
     * @return 结果
     */
    @Override
    @Transactional
    public boolean returnInventory(InventoryAction action) {
        int maxRetries = 3;
        int retryCount = 0;
        
        while (retryCount < maxRetries) {
            // 重新查询获取最新版本
            LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MaterialInventory::getMaterialId, action.getMaterialId())
                .eq(MaterialInventory::getWarehouseId, action.getWarehouseId())
                .last("limit 1");
            MaterialInventory inventory = getOne(wrapper);
            if (inventory == null) {
                throw new ServiceException("该物资在该仓库没有库存");
            }
            // 检查已使用数量是否充足
            if (inventory.getUsedQty().compareTo(action.getQty()) < 0) {
                throw new ServiceException("已使用库存不足，当前已使用：" + inventory.getUsedQty() + "，需要归还：" + action.getQty());
            }
            // 更新库存：减少已使用数量，增加总数量和可用数量
            inventory.setUsedQty(inventory.getUsedQty().subtract(action.getQty()));
            inventory.setTotalQty(inventory.getTotalQty().add(action.getQty()));
            inventory.setAvailableQty(this.calculateAvailableQty(inventory));
            inventory.setUpdateBy(SecurityUtils.getUsername());
            boolean result = this.updateById(inventory);
            
            // 如果更新成功，说明乐观锁通过
            if (result) {
                // 记录使用记录
                this.recordUsage(action.getMaterialId(), action.getWarehouseId(), action.getQty(), "return", action.getEventId(), action.getTaskId(), action.getRemark());
                return true;
            }
            
            // 更新失败，说明版本冲突，重试
            retryCount++;
            if (retryCount >= maxRetries) {
                throw new ServiceException("库存操作冲突，请稍后重试");
            }
            // 短暂等待后重试
            try {
                Thread.sleep(50 * retryCount);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ServiceException("库存操作被中断");
            }
        }
        throw new ServiceException("库存操作失败，请稍后重试");
    }

    /**
     * 库存调整（用于盘点、损失、发现等非业务流操作）
     * 
     * @param adjustment 调整信息
     * @return 结果
     */
    @Override
    @Transactional
    public boolean adjustInventory(InventoryAdjustment adjustment) {
        // 校验必填字段（validation annotations handle null checks, but keep for programmatic calls)
        if (adjustment.getMaterialId() == null) {
            throw new ServiceException("物资ID不能为空");
        }
        if (adjustment.getWarehouseId() == null) {
            throw new ServiceException("仓库ID不能为空");
        }
        if (adjustment.getAdjustmentQty() == null) {
            throw new ServiceException("调整数量不能为空");
        }
        if (adjustment.getReason() == null || adjustment.getReason().trim().isEmpty()) {
            throw new ServiceException("调整原因不能为空");
        }
        
        // Dictionary validation is optional - dictionary is mainly for frontend dropdown
        // Backend accepts any non-empty reason for flexibility
        // Frontend can use 'inventory_adjustment_reason' dict for better UX
        
        int maxRetries = 3;
        int retryCount = 0;
        
        while (retryCount < maxRetries) {
            // 查询库存记录
            LambdaQueryWrapper<MaterialInventory> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MaterialInventory::getMaterialId, adjustment.getMaterialId())
                .eq(MaterialInventory::getWarehouseId, adjustment.getWarehouseId())
                .last("limit 1");
            MaterialInventory inventory = getOne(wrapper);
            
            if (inventory == null) {
                // 如果库存不存在且是减少操作，报错
                if (adjustment.getAdjustmentQty().compareTo(BigDecimal.ZERO) < 0) {
                    throw new ServiceException("该物资在该仓库没有库存，无法进行减少调整");
                }
                // 如果是增加操作，创建新记录
                inventory = new MaterialInventory();
                inventory.setMaterialId(adjustment.getMaterialId());
                inventory.setWarehouseId(adjustment.getWarehouseId());
                inventory.setTotalQty(adjustment.getAdjustmentQty());
                inventory.setLockedQty(BigDecimal.ZERO);
                inventory.setUsedQty(BigDecimal.ZERO);
                inventory.setAvailableQty(adjustment.getAdjustmentQty());
                inventory.setVersion(1);
                inventory.setCreateBy(SecurityUtils.getUsername());
                boolean result = this.save(inventory);
                if (result) {
                    // 记录调整记录
                    this.recordUsage(adjustment.getMaterialId(), adjustment.getWarehouseId(), adjustment.getAdjustmentQty(), "adjust", null, null, 
                        "原因：" + adjustment.getReason() + (adjustment.getRemark() != null ? "；备注：" + adjustment.getRemark() : ""));
                    return true;
                }
                // 保存失败，可能是并发创建，重试
                retryCount++;
                if (retryCount >= maxRetries) {
                    throw new ServiceException("库存操作冲突，请稍后重试");
                }
                try {
                    Thread.sleep(50 * retryCount);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new ServiceException("库存操作被中断");
                }
                continue;
            }
            
            // 计算新的总数量
            BigDecimal newTotalQty = inventory.getTotalQty().add(adjustment.getAdjustmentQty());
            
            // 验证调整后不会导致负数
            if (newTotalQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("调整后总数量不能为负数，当前：" + inventory.getTotalQty() + "，调整：" + adjustment.getAdjustmentQty());
            }
            
            // 验证调整后不会导致可用数量为负数（考虑锁定和已使用数量）
            BigDecimal newAvailableQty = newTotalQty.subtract(inventory.getLockedQty()).subtract(inventory.getUsedQty());
            if (newAvailableQty.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("调整后可用数量不能为负数，当前总数量：" + inventory.getTotalQty() + 
                    "，锁定：" + inventory.getLockedQty() + "，已使用：" + inventory.getUsedQty() + 
                    "，调整：" + adjustment.getAdjustmentQty());
            }
            
            // 应用调整
            inventory.setTotalQty(newTotalQty);
            inventory.setAvailableQty(newAvailableQty);
            inventory.setUpdateBy(SecurityUtils.getUsername());
            boolean result = this.updateById(inventory);
            
            // 如果更新成功，说明乐观锁通过
            if (result) {
                // 记录调整记录
                String fullRemark = "原因：" + adjustment.getReason();
                if (adjustment.getRemark() != null && !adjustment.getRemark().trim().isEmpty()) {
                    fullRemark += "；备注：" + adjustment.getRemark();
                }
                recordUsage(adjustment.getMaterialId(), adjustment.getWarehouseId(), adjustment.getAdjustmentQty(), "adjust", null, null, fullRemark);
                return true;
            }
            
            // 更新失败，说明版本冲突，重试
            retryCount++;
            if (retryCount >= maxRetries) {
                throw new ServiceException("库存操作冲突，请稍后重试");
            }
            // 短暂等待后重试
            try {
                Thread.sleep(50 * retryCount);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ServiceException("库存操作被中断");
            }
        }
        throw new ServiceException("库存操作失败，请稍后重试");
    }

    /**
     * 计算可用数量
     * available_qty = total_qty - locked_qty - used_qty
     */
    private BigDecimal calculateAvailableQty(MaterialInventory inventory) {
        BigDecimal total = inventory.getTotalQty() != null ? inventory.getTotalQty() : BigDecimal.ZERO;
        BigDecimal locked = inventory.getLockedQty() != null ? inventory.getLockedQty() : BigDecimal.ZERO;
        BigDecimal used = inventory.getUsedQty() != null ? inventory.getUsedQty() : BigDecimal.ZERO;
        return total.subtract(locked).subtract(used);
    }

    /**
     * 记录使用记录
     */
    private void recordUsage(Long materialId, Long warehouseId, BigDecimal qty, String action, Long eventId, Long taskId, String remark) {
        MaterialUsage usage = new MaterialUsage();
        usage.setMaterialId(materialId);
        usage.setWarehouseId(warehouseId);
        usage.setQty(qty);
        usage.setAction(action);
        usage.setEventId(eventId);
        usage.setTaskId(taskId);
        usage.setRemark(remark);
        usageService.save(usage);
    }

    /**
     * 批量填充仓库名称（避免N+1查询问题）
     * 
     * @param inventoryList 库存列表
     */
    private void fillWarehouseNames(List<MaterialInventory> inventories) {
        if (CollectionUtils.isEmpty(inventories)) {
            return;
        }
        
        // 收集所有唯一的仓库ID
        Set<Long> warehouseIds = inventories.stream()
            .map(MaterialInventory::getWarehouseId)
            .filter(id -> id != null)
            .collect(Collectors.toSet());
        
        if (warehouseIds.isEmpty()) {
            return;
        }
        
        // 批量查询所有仓库（一次查询）
        List<Warehouse> warehouses = warehouseMapper.selectBatchIds(new ArrayList<>(warehouseIds));
        
        // 构建仓库ID到仓库名称的映射
        Map<Long, String> warehouseNameMap = warehouses.stream()
            .filter(warehouse -> warehouse != null)
            .collect(Collectors.toMap(
                Warehouse::getId,
                Warehouse::getWarehouseName,
                (existing, replacement) -> existing // 如果有重复ID，保留第一个
            ));
        
        // 填充仓库名称
        for (MaterialInventory each : inventories) {
            if (each.getWarehouseId() != null) {
                String warehouseName = warehouseNameMap.get(each.getWarehouseId());
                if (warehouseName != null) {
                    each.setWarehouseName(warehouseName);
                }
            }
        }
    }
}