package com.erp.warehouse.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.erp.warehouse.dto.StockDTO;
import com.erp.warehouse.dto.StockLogDTO;
import com.erp.warehouse.entity.Stock;
import com.erp.warehouse.entity.StockLog;
import com.erp.warehouse.entity.Warehouse;
import com.erp.warehouse.mapper.StockLogMapper;
import com.erp.warehouse.mapper.StockMapper;
import com.erp.warehouse.mapper.WarehouseMapper;
import com.erp.warehouse.service.StockService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.UUID;

/**
 * ERP本地库存服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StockServiceImpl implements StockService {

    private final StockMapper stockMapper;
    private final StockLogMapper stockLogMapper;
    private final WarehouseMapper warehouseMapper;

    private static final int MAX_RETRY = 5;

    // ==================== 库存查询 ====================

    @Override
    public IPage<StockDTO> listStocks(String skuCode, String skuName, Long warehouseId, Boolean lowStock, int page, int limit) {
        Page<Stock> pageParam = new Page<>(page, limit);
        LambdaQueryWrapper<Stock> queryWrapper = new LambdaQueryWrapper<>();

        if (skuCode != null && !skuCode.isEmpty()) {
            queryWrapper.like(Stock::getSkuCode, skuCode);
        }
        if (skuName != null && !skuName.isEmpty()) {
            queryWrapper.like(Stock::getSkuName, skuName);
        }
        if (warehouseId != null) {
            queryWrapper.eq(Stock::getWarehouseId, warehouseId);
        }
        if (Boolean.TRUE.equals(lowStock)) {
            // 可用库存 <= 预警阈值 (直接使用列名字符串)
            queryWrapper.apply("quantity - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0) <= LEAST(COALESCE(warning_threshold, 0), 10)");
        }

        queryWrapper.orderByDesc(Stock::getUpdateTime);

        IPage<Stock> pageResult = stockMapper.selectPage(pageParam, queryWrapper);

        return pageResult.convert(this::convertToDTO);
    }

    @Override
    public StockDTO getStock(String skuCode, Long warehouseId) {
        Stock stock = stockMapper.findBySkuAndWarehouse(skuCode, warehouseId);
        if (stock == null) {
            throw new IllegalArgumentException("库存记录不存在: SKU=" + skuCode + ", warehouseId=" + warehouseId);
        }
        return convertToDTO(stock);
    }

    @Override
    public IPage<StockLogDTO> listLogs(String skuCode, Long warehouseId, String changeType,
                                        Long orderId, String startDate, String endDate, int page, int limit) {
        if (startDate == null) startDate = "1970-01-01";
        if (endDate == null) endDate = "2099-12-31";

        Page<StockLog> pageParam = new Page<>(page, limit);
        IPage<StockLog> pageResult = stockLogMapper.selectLogsPage(
                pageParam, skuCode, warehouseId, changeType, orderId, startDate, endDate + " 23:59:59");

        return pageResult.convert(this::convertLogToDTO);
    }

    // ==================== 库存操作 ====================

    @Override
    @Transactional
    public Stock inbound(String skuCode, Long warehouseId, Integer quantity, String relatedNo, String remark, Long operatorId) {
        if (quantity <= 0) throw new IllegalArgumentException("入库数量必须为正数");
        validateWarehouse(warehouseId);

        Stock stock = getOrCreateStock(skuCode, warehouseId);
        int beforeQty = stock.getQuantity() != null ? stock.getQuantity() : 0;

        // 乐观锁重试
        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.incrementQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        // 记录流水
        createLog(skuCode, warehouseId, "inbound", quantity, beforeQty,
                beforeQty + quantity, null, relatedNo, null, remark, operatorId);

        log.info("手动入库成功: skuCode={}, warehouseId={}, quantity={}", skuCode, warehouseId, quantity);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock outbound(String skuCode, Long warehouseId, Integer quantity, String relatedNo, String remark, Long operatorId) {
        if (quantity <= 0) throw new IllegalArgumentException("出库数量必须为正数");
        validateWarehouse(warehouseId);

        Stock stock = getOrCreateStock(skuCode, warehouseId);
        int beforeQty = stock.getQuantity() != null ? stock.getQuantity() : 0;
        int available = stock.getAvailableQuantity();

        if (available < quantity) {
            throw new IllegalStateException("商品 " + skuCode + " 可用库存不足，当前可用" + available + "，需出库" + quantity);
        }

        // 乐观锁重试
        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.decrementQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        // 记录流水
        createLog(skuCode, warehouseId, "outbound", -quantity, beforeQty,
                beforeQty - quantity, null, relatedNo, null, remark, operatorId);

        log.info("手动出库成功: skuCode={}, warehouseId={}, quantity={}", skuCode, warehouseId, quantity);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock defectiveInbound(String skuCode, Long warehouseId, Integer quantity, String relatedNo, String remark, Long orderId) {
        if (quantity <= 0) throw new IllegalArgumentException("残次品数量必须为正数");
        validateWarehouse(warehouseId);

        Stock stock = getOrCreateStock(skuCode, warehouseId);
        int beforeQty = stock.getQuantity() != null ? stock.getQuantity() : 0;

        // 乐观锁重试
        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            // 残次品入库：总库存增加，残次品数量增加
            int updated1 = stockMapper.incrementQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated1 == 0) break;
            stock = refreshStock(skuCode, warehouseId);
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated2 = stockMapper.incrementDefectiveQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated2 > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "defective_inbound", quantity, beforeQty,
                beforeQty + quantity, orderId, relatedNo, null, remark, null);

        log.info("残次品入库成功: skuCode={}, warehouseId={}, quantity={}", skuCode, warehouseId, quantity);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock adjust(String skuCode, Long warehouseId, Integer quantity, Integer defectiveQuantity, String remark, Long operatorId) {
        validateWarehouse(warehouseId);

        Stock stock = getOrCreateStock(skuCode, warehouseId);
        int beforeQty = stock.getQuantity() != null ? stock.getQuantity() : 0;

        if (quantity != null) {
            for (int i = 0; i < MAX_RETRY; i++) {
                stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
                int diff = quantity - beforeQty;
                int updated;
                if (diff > 0) {
                    updated = stockMapper.incrementQuantity(skuCode, warehouseId, diff, stock.getVersion());
                } else if (diff < 0) {
                    updated = stockMapper.decrementQuantity(skuCode, warehouseId, -diff, stock.getVersion());
                } else {
                    updated = 1;
                }
                if (updated > 0) break;
                stock = refreshStock(skuCode, warehouseId);
            }
        }

        if (defectiveQuantity != null) {
            stock = refreshStock(skuCode, warehouseId);
            int beforeDefective = stock.getDefectiveQuantity() != null ? stock.getDefectiveQuantity() : 0;
            int diff = defectiveQuantity - beforeDefective;
            if (diff != 0) {
                for (int i = 0; i < MAX_RETRY; i++) {
                    stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
                    int updated = stock.getDefectiveQuantity() < defectiveQuantity
                            ? stockMapper.incrementDefectiveQuantity(skuCode, warehouseId, diff, stock.getVersion())
                            : 0;
                    if (updated > 0) break;
                    stock = refreshStock(skuCode, warehouseId);
                }
            }
        }

        int afterQty = quantity != null ? quantity : beforeQty;
        createLog(skuCode, warehouseId, "adjust", afterQty - beforeQty, beforeQty, afterQty,
                null, null, null, remark, operatorId);

        log.info("库存调整成功: skuCode={}, warehouseId={}, quantity={}", skuCode, warehouseId, quantity);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock updateThreshold(String skuCode, Long warehouseId, Integer warningThreshold) {
        Stock stock = stockMapper.findBySkuAndWarehouse(skuCode, warehouseId);
        if (stock == null) {
            // 自动创建
            stock = Stock.builder()
                    .skuCode(skuCode)
                    .warehouseId(warehouseId)
                    .quantity(0)
                    .lockedQuantity(0)
                    .reservedQuantity(0)
                    .defectiveQuantity(0)
                    .warningThreshold(warningThreshold)
                    .version(0)
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();
            stockMapper.insert(stock);
        } else {
            stock.setWarningThreshold(warningThreshold);
            stock.setUpdateTime(LocalDateTime.now());
            stockMapper.updateById(stock);
        }
        return stock;
    }

    // ==================== 订单联动 ====================

    @Override
    @Transactional
    public Stock lock(String skuCode, Long warehouseId, Integer quantity, Long orderId) {
        if (quantity <= 0) throw new IllegalArgumentException("占用数量必须为正数");

        Stock stock = getOrCreateStock(skuCode, warehouseId);
        int available = stock.getAvailableQuantity();

        if (available < quantity) {
            throw new IllegalStateException("商品 " + skuCode + " 可用库存不足，当前可用" + available + "，需占用" + quantity);
        }

        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.incrementLockedQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "lock", quantity, stock.getQuantity(),
                stock.getQuantity(), orderId, null, null, "锁定库存，订单ID " + orderId, null);

        log.info("锁定库存成功: skuCode={}, warehouseId={}, quantity={}, orderId={}", skuCode, warehouseId, quantity, orderId);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock unlock(String skuCode, Long warehouseId, Integer quantity, Long orderId) {
        if (quantity <= 0) throw new IllegalArgumentException("解锁数量必须为正数");

        Stock stock = refreshStock(skuCode, warehouseId);
        if (stock.getLockedQuantity() < quantity) {
            throw new IllegalStateException("商品 " + skuCode + " 锁定库存不足，当前锁定" + stock.getLockedQuantity() + "，需解锁" + quantity);
        }

        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.decrementLockedQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "unlock", -quantity, stock.getQuantity(),
                stock.getQuantity(), orderId, null, null, "解锁库存，订单ID " + orderId, null);

        log.info("解锁库存成功: skuCode={}, warehouseId={}, quantity={}, orderId={}", skuCode, warehouseId, quantity, orderId);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public ReserveResult reserve(String skuCode, Long warehouseId, Integer quantity, Long orderId, String reserveToken) {
        if (quantity <= 0) throw new IllegalArgumentException("预扣数量必须为正数");

        Stock stock = getOrCreateStock(skuCode, warehouseId);
        int available = stock.getAvailableQuantity();

        if (available < quantity) {
            throw new IllegalStateException("商品 " + skuCode + " 可用库存不足，当前可用" + available + "，需预扣" + quantity);
        }

        String token = reserveToken != null ? reserveToken
                : "RSV_" + orderId + "_" + skuCode + "_" + System.currentTimeMillis();

        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.incrementReservedQuantity(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "reserve", quantity, stock.getQuantity(),
                stock.getQuantity(), orderId, null, null, "预扣库存，订单ID " + orderId + "，凭证 " + token, null);

        log.info("预扣库存成功: skuCode={}, warehouseId={}, quantity={}, orderId={}, token={}", skuCode, warehouseId, quantity, orderId, token);
        stock = refreshStock(skuCode, warehouseId);
        return new ReserveResult(stock, token);
    }

    @Override
    @Transactional
    public Stock confirmReserve(String reserveToken, Long orderId, String trackingNo) {
        StockLog logRecord = stockLogMapper.findByReserveToken(reserveToken, orderId);
        if (logRecord == null) {
            throw new IllegalArgumentException("找不到预扣凭证 " + reserveToken + " 的记录");
        }

        String skuCode = logRecord.getSkuCode();
        Long warehouseId = logRecord.getWarehouseId();
        Integer quantity = Math.abs(logRecord.getChangeQuantity());

        Stock stock = refreshStock(skuCode, warehouseId);
        if (stock.getReservedQuantity() < quantity) {
            throw new IllegalStateException("预扣库存不足，当前预扣" + stock.getReservedQuantity() + "，需确认" + quantity);
        }

        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.confirmReserveOutbound(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "confirm", -quantity, stock.getQuantity(),
                stock.getQuantity() - quantity, orderId, null, trackingNo,
                "确认预扣出库，凭证 " + reserveToken + "，运单号 " + trackingNo, null);

        log.info("确认预扣出库成功: token={}, orderId={}, quantity={}", reserveToken, orderId, quantity);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock cancelReserve(String reserveToken, Long orderId) {
        StockLog logRecord = stockLogMapper.findByReserveToken(reserveToken, orderId);
        if (logRecord == null) {
            throw new IllegalArgumentException("找不到预扣凭证 " + reserveToken + " 的记录");
        }

        String skuCode = logRecord.getSkuCode();
        Long warehouseId = logRecord.getWarehouseId();
        Integer quantity = Math.abs(logRecord.getChangeQuantity());

        Stock stock = refreshStock(skuCode, warehouseId);
        if (stock.getReservedQuantity() < quantity) {
            throw new IllegalStateException("预扣库存不足，无法取消");
        }

        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.cancelReserveRelease(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "cancel", 0, stock.getQuantity(),
                stock.getQuantity(), orderId, null, null, "取消预扣释放库存，凭证 " + reserveToken, null);

        log.info("取消预扣成功: token={}, orderId={}", reserveToken, orderId);
        return refreshStock(skuCode, warehouseId);
    }

    @Override
    @Transactional
    public Stock ship(String skuCode, Long warehouseId, Integer quantity, Long orderId, String trackingNo) {
        if (quantity <= 0) throw new IllegalArgumentException("出库数量必须为正数");

        Stock stock = refreshStock(skuCode, warehouseId);
        if (stock.getLockedQuantity() < quantity) {
            throw new IllegalStateException("商品 " + skuCode + " 锁定库存不足，当前锁定" + stock.getLockedQuantity() + "，需出库" + quantity);
        }

        for (int i = 0; i < MAX_RETRY; i++) {
            stock.setVersion(stock.getVersion() != null ? stock.getVersion() : 0);
            int updated = stockMapper.shipOutbound(skuCode, warehouseId, quantity, stock.getVersion());
            if (updated > 0) break;
            stock = refreshStock(skuCode, warehouseId);
        }

        createLog(skuCode, warehouseId, "ship", -quantity, stock.getQuantity(),
                stock.getQuantity() - quantity, orderId, null, trackingNo,
                "发货出库，订单ID " + orderId + "，运单号 " + trackingNo, null);

        log.info("发货出库成功: skuCode={}, warehouseId={}, quantity={}, orderId={}, trackingNo={}",
                skuCode, warehouseId, quantity, orderId, trackingNo);
        return refreshStock(skuCode, warehouseId);
    }

    // ==================== 私有辅助方法 ====================

    private Stock getOrCreateStock(String skuCode, Long warehouseId) {
        Stock stock = stockMapper.findBySkuAndWarehouse(skuCode, warehouseId);
        if (stock == null) {
            stock = Stock.builder()
                    .skuCode(skuCode)
                    .skuName("未知商品")
                    .warehouseId(warehouseId)
                    .quantity(0)
                    .lockedQuantity(0)
                    .reservedQuantity(0)
                    .defectiveQuantity(0)
                    .warningThreshold(10)
                    .version(0)
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();
            stockMapper.insert(stock);
        }
        return stock;
    }

    private Stock refreshStock(String skuCode, Long warehouseId) {
        Stock stock = stockMapper.findBySkuAndWarehouse(skuCode, warehouseId);
        if (stock == null) {
            throw new IllegalStateException("库存记录不存在，刷新失败");
        }
        return stock;
    }

    private void validateWarehouse(Long warehouseId) {
        Warehouse warehouse = warehouseMapper.selectById(warehouseId);
        if (warehouse == null) {
            throw new IllegalArgumentException("仓库不存在: " + warehouseId);
        }
    }

    private void createLog(String skuCode, Long warehouseId, String changeType, Integer changeQuantity,
                          Integer beforeQty, Integer afterQty, Long orderId, String relatedNo,
                          String deliveryNo, String remark, Long operatorId) {
        StockLog log = StockLog.builder()
                .skuCode(skuCode)
                .warehouseId(warehouseId)
                .changeType(changeType)
                .beforeQuantity(beforeQty)
                .changeQuantity(changeQuantity)
                .afterQuantity(afterQty)
                .orderId(orderId)
                .relatedNo(relatedNo)
                .deliveryNo(deliveryNo)
                .remark(remark)
                .operatorId(operatorId)
                .createTime(LocalDateTime.now())
                .build();
        stockLogMapper.insert(log);
    }

    private StockDTO convertToDTO(Stock stock) {
        Warehouse warehouse = warehouseMapper.selectById(stock.getWarehouseId());
        return StockDTO.builder()
                .id(stock.getId())
                .skuCode(stock.getSkuCode())
                .skuName(stock.getSkuName())
                .warehouseId(stock.getWarehouseId())
                .warehouseName(warehouse != null ? warehouse.getName() : null)
                .quantity(stock.getQuantity())
                .lockedQuantity(stock.getLockedQuantity())
                .reservedQuantity(stock.getReservedQuantity())
                .availableQuantity(stock.getAvailableQuantity())
                .defectiveQuantity(stock.getDefectiveQuantity())
                .warningThreshold(stock.getWarningThreshold())
                .isLow(stock.isLowStock())
                .createTime(stock.getCreateTime())
                .updateTime(stock.getUpdateTime())
                .build();
    }

    private StockLogDTO convertLogToDTO(StockLog log) {
        Warehouse warehouse = warehouseMapper.selectById(log.getWarehouseId());
        return StockLogDTO.builder()
                .id(log.getId())
                .skuCode(log.getSkuCode())
                .warehouseId(log.getWarehouseId())
                .warehouseName(warehouse != null ? warehouse.getName() : null)
                .changeQuantity(log.getChangeQuantity())
                .changeType(log.getChangeType())
                .typeLabel(log.getTypeLabel())
                .relatedNo(log.getRelatedNo())
                .orderId(log.getOrderId())
                .deliveryNo(log.getDeliveryNo())
                .remark(log.getRemark())
                .createTime(log.getCreateTime())
                .build();
    }
}
