package com.haha.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haha.common.dto.FloorConfig;
import com.haha.common.dto.GoodsItem;
import com.haha.common.exception.BusinessException;
import com.haha.entity.Device;
import com.haha.entity.DeviceInventory;
import com.haha.entity.InventoryLog;
import com.haha.entity.LayerTemplate;
import com.haha.entity.dto.InventoryQueryDTO;
import com.haha.mapper.DeviceInventoryMapper;
import com.haha.mapper.DeviceMapper;
import com.haha.mapper.LayerTemplateMapper;
import com.haha.service.DeviceInventoryService;
import com.haha.service.InventoryLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 设备库存服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceInventoryServiceImpl extends ServiceImpl<DeviceInventoryMapper, DeviceInventory>
        implements DeviceInventoryService {

    private final DeviceInventoryMapper deviceInventoryMapper;

    private final InventoryLogService inventoryLogService;

    private final DeviceMapper deviceMapper;

    private final LayerTemplateMapper layerTemplateMapper;

    @Override
    public IPage<DeviceInventory> getPage(int page, int pageSize, InventoryQueryDTO queryDTO) {
        LambdaQueryWrapper<DeviceInventory> wrapper = new LambdaQueryWrapper<>();

        // 设备ID筛选
        if (StringUtils.hasText(queryDTO.getDeviceId())) {
            wrapper.eq(DeviceInventory::getDeviceId, queryDTO.getDeviceId());
        }

        // 商品名称模糊搜索
        if (StringUtils.hasText(queryDTO.getProductName())) {
            wrapper.like(DeviceInventory::getProductName, queryDTO.getProductName());
        }

        // 低库存筛选
        if (Boolean.TRUE.equals(queryDTO.getLowStock())) {
            wrapper.apply("stock <= warning_threshold");
        }

        // 按更新时间倒序
        wrapper.orderByDesc(DeviceInventory::getUpdateTime);

        IPage<DeviceInventory> result = page(new Page<>(page, pageSize), wrapper);
        fillDeviceNames(result.getRecords());
        return result;
    }

    private void fillDeviceNames(List<DeviceInventory> records) {
        if (records == null || records.isEmpty()) return;
        List<String> ids = records.stream()
                .map(DeviceInventory::getDeviceId)
                .filter(id -> id != null && !id.isEmpty())
                .distinct()
                .collect(java.util.stream.Collectors.toList());
        if (ids.isEmpty()) return;
        List<Device> devices = deviceMapper.selectList(
                new LambdaQueryWrapper<Device>().in(Device::getDeviceId, ids));
        if (devices == null) return;
        java.util.Map<String, String> nameMap = devices.stream()
                .collect(java.util.stream.Collectors.toMap(Device::getDeviceId, d -> d.getName() != null ? d.getName() : "", (a, b) -> a));
        for (DeviceInventory r : records) {
            r.setDeviceName(nameMap.getOrDefault(r.getDeviceId(), ""));
        }
    }

    @Override
    public List<Map<String, Object>> getInventoryWithProduct(String deviceId) {
        return deviceInventoryMapper.selectInventoryWithProduct(deviceId);
    }

    @Override
    public List<Map<String, Object>> getLowStockList() {
        return deviceInventoryMapper.selectLowStockInventories();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DeviceInventory increaseStock(String deviceId, Long productId, String productCode, String productName,
                                         Integer quantity, Integer shelfNum, String position,
                                         Long operatorId, String operatorName, String activityId) {
        // 查询现有库存
        DeviceInventory inventory = deviceInventoryMapper.selectByDeviceAndProduct(deviceId, productId);

        int beforeStock = 0;
        if (inventory == null) {
            // 新建库存记录
            inventory = new DeviceInventory();
            inventory.setDeviceId(deviceId);
            inventory.setProductId(productId);
            inventory.setProductCode(productCode);
            inventory.setProductName(productName);
            inventory.setStock(quantity);
            inventory.setShelfNum(shelfNum);
            inventory.setPosition(position);
            inventory.setWarningThreshold(5); // 默认预警阈值
            inventory.setLastRestockTime(LocalDateTime.now());
            inventory.setCreateTime(LocalDateTime.now());
            inventory.setUpdateTime(LocalDateTime.now());
            save(inventory);
            beforeStock = 0;
        } else {
            // 更新库存
            beforeStock = inventory.getStock();
            inventory.setStock(beforeStock + quantity);
            inventory.setShelfNum(shelfNum);
            inventory.setPosition(position);
            inventory.setLastRestockTime(LocalDateTime.now());
            inventory.setUpdateTime(LocalDateTime.now());
            updateById(inventory);
        }

        // 记录日志
        inventoryLogService.logChange(deviceId, productId, productCode, productName,
                InventoryLog.TYPE_RESTOCK, quantity, beforeStock, inventory.getStock(),
                null, activityId, operatorId, operatorName, "上货增加库存");

        return inventory;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DeviceInventory decreaseStock(String deviceId, Long productId, Integer quantity,
                                         String orderId, Long operatorId, String operatorName) {
        DeviceInventory inventory = deviceInventoryMapper.selectByDeviceAndProduct(deviceId, productId);

        if (inventory == null) {
            log.warn("库存记录不存在: deviceId={}, productId={}", deviceId, productId);
            throw new BusinessException(404, "库存记录不存在");
        }

        int beforeStock = inventory.getStock();
        int newStock = beforeStock - quantity;
        inventory.setStock(newStock);
        inventory.setUpdateTime(LocalDateTime.now());

        boolean updated = updateById(inventory);
        if (!updated) {
            throw new BusinessException(409, "库存数据并发冲突，请重试");
        }

        // 记录日志
        inventoryLogService.logChange(deviceId, productId, inventory.getProductCode(), inventory.getProductName(),
                InventoryLog.TYPE_SALE, -quantity, beforeStock, newStock,
                orderId, null, operatorId, operatorName, "销售扣减库存");

        return inventory;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DeviceInventory adjustStock(String deviceId, Long productId, Integer newStock,
                                       String remark, Long operatorId, String operatorName) {
        DeviceInventory inventory = deviceInventoryMapper.selectByDeviceAndProduct(deviceId, productId);

        if (inventory == null) {
            log.warn("库存记录不存在: deviceId={}, productId={}", deviceId, productId);
            throw new BusinessException(404, "库存记录不存在");
        }

        int beforeStock = inventory.getStock();
        int changeQuantity = newStock - beforeStock;
        int changeType = changeQuantity >= 0 ? InventoryLog.TYPE_ADJUST_ADD : InventoryLog.TYPE_ADJUST_SUB;

        inventory.setStock(newStock);
        inventory.setUpdateTime(LocalDateTime.now());

        boolean updated = updateById(inventory);
        if (!updated) {
            throw new BusinessException(409, "库存数据并发冲突，请重试");
        }

        // 记录日志
        inventoryLogService.logChange(deviceId, productId, inventory.getProductCode(), inventory.getProductName(),
                changeType, changeQuantity, beforeStock, newStock,
                null, null, operatorId, operatorName, remark);

        return inventory;
    }

    @Override
    public Map<String, Object> getStatistics() {
        Map<String, Object> stats = new HashMap<>();

        // 总库存记录数
        stats.put("totalRecords", count());

        // 总库存数量（使用SQL聚合，避免大数据量内存求和）
        Integer totalStock = deviceInventoryMapper.selectSumStock();
        stats.put("totalStock", totalStock != null ? totalStock : 0);

        // 低库存数量
        long lowStockCount = count(new LambdaQueryWrapper<DeviceInventory>()
                .apply("stock <= warning_threshold"));
        stats.put("lowStockCount", lowStockCount);

        // 零库存数量
        long zeroStockCount = count(new LambdaQueryWrapper<DeviceInventory>()
                .eq(DeviceInventory::getStock, 0));
        stats.put("zeroStockCount", zeroStockCount);

        return stats;
    }

    @Override
    public DeviceInventory getByDeviceAndProduct(String deviceId, Long productId) {
        return deviceInventoryMapper.selectByDeviceAndProduct(deviceId, productId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recalculateStandardStock(String deviceId) {
        // 1. 查询该设备的所有层模版
        List<LayerTemplate> templates = layerTemplateMapper.selectByDeviceId(deviceId);
        if (templates == null || templates.isEmpty()) {
            // 无层模版，清空所有标准库存
            LambdaUpdateWrapper<DeviceInventory> clearWrapper = new LambdaUpdateWrapper<DeviceInventory>()
                    .eq(DeviceInventory::getDeviceId, deviceId);
            DeviceInventory clearRecord = new DeviceInventory();
            clearRecord.setStandardStock(0);
            update(clearRecord, clearWrapper);
            log.info("标准库存重算完成（无层模版）: deviceId={}, 标准库存全部置0", deviceId);
            return;
        }

        // 2. 解析层模版，按 productCode 累加标准库存
        Map<String, Integer> standardMap = new HashMap<>();
        ObjectMapper mapper = new ObjectMapper();

        for (LayerTemplate template : templates) {
            accumulateFromFloors(mapper, template.getLeftFloors(), standardMap);
            accumulateFromFloors(mapper, template.getRightFloors(), standardMap);
        }

        // 3. 查询设备所有库存记录并更新
        List<DeviceInventory> inventoryList = list(
                new LambdaQueryWrapper<DeviceInventory>()
                        .eq(DeviceInventory::getDeviceId, deviceId));

        if (inventoryList != null) {
            for (DeviceInventory inv : inventoryList) {
                String code = inv.getProductCode();
                Integer std = standardMap.get(code);
                inv.setStandardStock(std != null ? std : 0);
                updateById(inv);
            }
        }

        log.info("标准库存重算完成: deviceId={}, 商品数={}, 有层模版标准的={}",
                deviceId, inventoryList != null ? inventoryList.size() : 0, standardMap.size());
    }

    /**
     * 从层模版JSON中解析商品标准库存并累加到map
     */
    private void accumulateFromFloors(ObjectMapper mapper, String floorsJson, Map<String, Integer> standardMap) {
        if (floorsJson == null || floorsJson.isEmpty()) return;
        try {
            List<FloorConfig> floors = mapper.readValue(floorsJson,
                    new TypeReference<List<FloorConfig>>() {});
            if (floors == null) return;
            for (FloorConfig floor : floors) {
                if (floor.getGoods() == null) continue;
                for (GoodsItem goods : floor.getGoods()) {
                    String code = goods.getKey();
                    if (code == null || code.isEmpty()) continue;
                    Integer templateStock = goods.getStock() != null ? goods.getStock() : 0;
                    standardMap.merge(code, templateStock, Integer::sum);
                }
            }
        } catch (Exception e) {
            log.warn("解析层模版JSON失败: {}", e.getMessage());
        }
    }

    @Override
    public IPage<Map<String, Object>> getDeviceInventoryStats(int page, int pageSize, InventoryQueryDTO queryDTO) {
        Page<Map<String, Object>> pageParam = new Page<>(page, pageSize);

        String shopName = queryDTO.getShopName();
        String deviceName = queryDTO.getDeviceName();
        String deviceId = queryDTO.getDeviceId();
        Boolean hasUnknownProduct = queryDTO.getHasUnknownProduct();
        Integer stockStatus = queryDTO.getStockStatus();

        IPage<Map<String, Object>> result = deviceInventoryMapper.selectDeviceInventoryStats(
                pageParam, shopName, deviceName, deviceId, hasUnknownProduct, stockStatus);

        // 处理每条记录，添加计算字段
        for (Map<String, Object> record : result.getRecords()) {
            // 计算库存占比
            Integer remainingStock = record.get("remaining_stock") != null ?
                    ((Number) record.get("remaining_stock")).intValue() : 0;
            Integer standardStock = record.get("standard_stock") != null ?
                    ((Number) record.get("standard_stock")).intValue() : 0;

            int stockRatio = standardStock > 0 ? (remainingStock * 100 / standardStock) : 0;
            record.put("stock_ratio", stockRatio);

            // 判断库存状态
            String stockStatusStr;
            Integer zeroStockCount = record.get("zero_stock_count") != null ?
                    ((Number) record.get("zero_stock_count")).intValue() : 0;
            Integer lowStockCount = record.get("low_stock_count") != null ?
                    ((Number) record.get("low_stock_count")).intValue() : 0;
            Integer productCount = record.get("product_count") != null ?
                    ((Number) record.get("product_count")).intValue() : 0;

            if (productCount == 0 || remainingStock == 0) {
                stockStatusStr = "缺货";
            } else if (lowStockCount > 0 || stockRatio < 50) {
                stockStatusStr = "低库存";
            } else {
                stockStatusStr = "正常";
            }
            record.put("stock_status", stockStatusStr);

            // 是否有未知商品
            Integer unknownProductCount = record.get("unknown_product_count") != null ?
                    ((Number) record.get("unknown_product_count")).intValue() : 0;
            record.put("has_unknown_product", unknownProductCount > 0);
        }

        return result;
    }
}
