package com.erp.purchase.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.erp.purchase.client.FinanceFeignClient;
import com.erp.purchase.client.WarehouseFeignClient;
import com.erp.purchase.dto.PurchaseInboundDTO;
import com.erp.purchase.entity.PurchaseInbound;
import com.erp.purchase.entity.PurchaseOrder;
import com.erp.purchase.entity.Supplier;
import com.erp.purchase.enums.PurchaseInboundStatus;
import com.erp.purchase.enums.PurchaseOrderStatus;
import com.erp.purchase.exception.BusinessException;
import com.erp.purchase.mapper.PurchaseInboundMapper;
import com.erp.purchase.mapper.PurchaseOrderMapper;
import com.erp.purchase.mapper.SupplierMapper;
import com.erp.purchase.service.PurchaseInboundService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PurchaseInboundServiceImpl implements PurchaseInboundService {

    private final PurchaseInboundMapper purchaseInboundMapper;
    private final PurchaseOrderMapper purchaseOrderMapper;
    private final SupplierMapper supplierMapper;
    private final WarehouseFeignClient warehouseClient;
    private final FinanceFeignClient financeClient;

    private static final AtomicLong INBOUND_COUNTER = new AtomicLong(System.currentTimeMillis() % 100000);

    @Override
    public IPage<PurchaseInbound> listPage(PurchaseInboundDTO.QueryRequest query) {
        Page<PurchaseInbound> page = new Page<>(query.getPage(), query.getLimit());
        return purchaseInboundMapper.selectPageList(page, query.getInboundNo(), query.getPurchaseOrderId(),
                query.getSupplierId(), query.getWarehouseId(), query.getStatus());
    }

    @Override
    public PurchaseInbound getById(Long id) {
        PurchaseInbound inbound = purchaseInboundMapper.selectById(id);
        if (inbound == null) throw new BusinessException("采购入库单不存在");
        return inbound;
    }

    @Override
    public PurchaseInbound getByInboundNo(String inboundNo) {
        return purchaseInboundMapper.findByInboundNo(inboundNo);
    }

    @Override
    @Transactional
    public PurchaseInbound create(PurchaseInboundDTO.CreateRequest request, Long operatorId) {
        PurchaseOrder order = purchaseOrderMapper.selectById(request.getPurchaseOrderId());
        if (order == null) throw new BusinessException("采购订单不存在");
        if (!PurchaseOrderStatus.APPROVED.getCode().equals(order.getStatus()) &&
            !PurchaseOrderStatus.PARTIAL_INBOUND.getCode().equals(order.getStatus()) &&
            !PurchaseOrderStatus.ALL_INBOUND.getCode().equals(order.getStatus())) {
            throw new BusinessException("采购订单状态不允许创建入库单");
        }

        Supplier supplier = supplierMapper.selectById(order.getSupplierId());
        if (supplier == null) throw new BusinessException("供应商不存在");

        String inboundNo = generateInboundNo();

        List<PurchaseInbound.PurchaseInboundItem> items = new ArrayList<>();
        int totalQty = 0;
        BigDecimal totalAmt = BigDecimal.ZERO;

        for (PurchaseInboundDTO.InboundItemRequest itemReq : request.getItems()) {
            int arrived = itemReq.getArrivedQuantity() != null ? itemReq.getArrivedQuantity() : 0;
            int accepted = itemReq.getAcceptedQuantity() != null ? itemReq.getAcceptedQuantity() : arrived;
            int defective = itemReq.getDefectiveQuantity() != null ? itemReq.getDefectiveQuantity() : 0;
            BigDecimal price = itemReq.getPrice() != null ? itemReq.getPrice() : BigDecimal.ZERO;
            BigDecimal amount = price.multiply(new BigDecimal(accepted + defective));

            PurchaseInbound.PurchaseInboundItem item = PurchaseInbound.PurchaseInboundItem.builder()
                    .skuCode(itemReq.getSkuCode())
                    .skuName(itemReq.getSkuName())
                    .unit(itemReq.getUnit())
                    .planQuantity(itemReq.getArrivedQuantity())
                    .arrivedQuantity(arrived)
                    .acceptedQuantity(accepted)
                    .defectiveQuantity(defective)
                    .price(price)
                    .amount(amount)
                    .remark(itemReq.getRemark())
                    .build();
            items.add(item);
            totalQty += (accepted + defective);
            totalAmt = totalAmt.add(amount);
        }

        PurchaseInbound inbound = PurchaseInbound.builder()
                .inboundNo(inboundNo)
                .purchaseOrderId(order.getId())
                .purchaseOrderNo(order.getOrderNo())
                .supplierId(order.getSupplierId())
                .supplierName(supplier.getName())
                .warehouseId(request.getWarehouseId() != null ? request.getWarehouseId() : order.getWarehouseId())
                .status(PurchaseInboundStatus.PENDING.getCode())
                .totalQuantity(totalQty)
                .acceptedQuantity(0)
                .defectiveQuantity(0)
                .totalAmount(totalAmt)
                .expectedTime(request.getExpectedTime())
                .remark(request.getRemark())
                .operatorId(operatorId)
                .createBy(operatorId)
                .createTime(LocalDateTime.now())
                .updateBy(operatorId)
                .updateTime(LocalDateTime.now())
                .deleted(0)
                .items(items)
                .build();

        purchaseInboundMapper.insert(inbound);
        log.info("创建采购入库单: inboundNo={}, orderNo={}, qty={}", inboundNo, order.getOrderNo(), totalQty);
        return inbound;
    }

    @Override
    @Transactional
    public PurchaseInbound confirmInbound(Long inboundId, PurchaseInboundDTO.ConfirmRequest request, Long operatorId) {
        PurchaseInbound inbound = purchaseInboundMapper.selectById(inboundId);
        if (inbound == null) throw new BusinessException("采购入库单不存在");
        if (!PurchaseInboundStatus.PENDING.getCode().equals(inbound.getStatus()) &&
            !PurchaseInboundStatus.PARTIAL.getCode().equals(inbound.getStatus())) {
            throw new BusinessException("入库单状态不允许确认");
        }

        PurchaseOrder order = purchaseOrderMapper.selectById(inbound.getPurchaseOrderId());
        if (order == null) throw new BusinessException("采购订单不存在");

        // 调用仓库服务执行实际入库
        try {
            WarehouseFeignClient.InboundRequest warehouseRequest = WarehouseFeignClient.InboundRequest.builder()
                    .relatedNo(inbound.getPurchaseOrderNo())
                    .inboundType("purchase")
                    .warehouseId(inbound.getWarehouseId())
                    .supplierId(inbound.getSupplierId())
                    .supplierName(inbound.getSupplierName())
                    .remark(request.getRemark())
                    .items(request.getItems().stream()
                            .map(i -> WarehouseFeignClient.InboundRequest.InboundItem.builder()
                                    .skuCode(i.getSkuCode())
                                    .planQuantity(i.getAcceptedQuantity() + (i.getDefectiveQuantity() != null ? i.getDefectiveQuantity() : 0))
                                    .acceptedQuantity(i.getAcceptedQuantity())
                                    .defectiveQuantity(i.getDefectiveQuantity() != null ? i.getDefectiveQuantity() : 0)
                                    .build())
                            .collect(Collectors.toList()))
                    .build();

            WarehouseFeignClient.InboundResponse resp = warehouseClient.createInbound(warehouseRequest, operatorId);
            inbound.setWarehouseInboundNo(resp.getInboundNo());
        } catch (Exception e) {
            log.warn("调用仓库服务创建入库单失败: inboundNo={}, error={}", inbound.getInboundNo(), e.getMessage());
        }

        // 计算入库数量
        int totalAccepted = 0;
        int totalDefective = 0;
        BigDecimal inboundAmt = BigDecimal.ZERO;

        for (PurchaseInboundDTO.ConfirmRequest.ConfirmItem ci : request.getItems()) {
            totalAccepted += (ci.getAcceptedQuantity() != null ? ci.getAcceptedQuantity() : 0);
            totalDefective += (ci.getDefectiveQuantity() != null ? ci.getDefectiveQuantity() : 0);

            // 按SKU查单价
            BigDecimal price = BigDecimal.ZERO;
            if (inbound.getItems() != null) {
                for (PurchaseInbound.PurchaseInboundItem item : inbound.getItems()) {
                    if (item.getSkuCode().equals(ci.getSkuCode())) {
                        price = item.getPrice();
                        break;
                    }
                }
            }
            int qty = (ci.getAcceptedQuantity() != null ? ci.getAcceptedQuantity() : 0)
                    + (ci.getDefectiveQuantity() != null ? ci.getDefectiveQuantity() : 0);
            inboundAmt = inboundAmt.add(price.multiply(new BigDecimal(qty)));
        }

        inbound.setAcceptedQuantity(totalAccepted);
        inbound.setDefectiveQuantity(totalDefective);
        inbound.setInboundTime(LocalDateTime.now());
        inbound.setStatus(PurchaseInboundStatus.COMPLETED.getCode());
        inbound.setUpdateBy(operatorId);
        inbound.setUpdateTime(LocalDateTime.now());
        purchaseInboundMapper.updateById(inbound);

        // 更新采购单入库状态
        updatePurchaseOrderInboundStatus(order, totalAccepted + totalDefective, inboundAmt);

        // 通知财务入库完成，更新应付账款
        try {
            financeClient.updateInboundStatus(inbound.getPurchaseOrderNo(), inbound.getInboundNo(),
                    inboundAmt, "inbound");
        } catch (Exception e) {
            log.warn("通知财务更新入库状态失败: orderNo={}, error={}", order.getOrderNo(), e.getMessage());
        }

        log.info("确认采购入库: inboundNo={}, accepted={}, defective={}", inbound.getInboundNo(), totalAccepted, totalDefective);
        return inbound;
    }

    @Override
    @Transactional
    public void cancel(Long inboundId, Long operatorId) {
        PurchaseInbound inbound = purchaseInboundMapper.selectById(inboundId);
        if (inbound == null) throw new BusinessException("采购入库单不存在");
        if (!PurchaseInboundStatus.PENDING.getCode().equals(inbound.getStatus())) {
            throw new BusinessException("只有待入库状态可以取消");
        }
        inbound.setStatus(PurchaseInboundStatus.CANCELLED.getCode());
        inbound.setUpdateBy(operatorId);
        inbound.setUpdateTime(LocalDateTime.now());
        purchaseInboundMapper.updateById(inbound);
        log.info("取消采购入库单: inboundNo={}", inbound.getInboundNo());
    }

    private void updatePurchaseOrderInboundStatus(PurchaseOrder order, int inboundQty, BigDecimal inboundAmt) {
        BigDecimal newInboundAmount = order.getInboundAmount().add(inboundAmt);

        // 判断入库完成度
        if (newInboundAmount.compareTo(order.getTotalWithTax()) >= 0) {
            order.setStatus(PurchaseOrderStatus.ALL_INBOUND.getCode());
        } else if (newInboundAmount.compareTo(BigDecimal.ZERO) > 0) {
            order.setStatus(PurchaseOrderStatus.PARTIAL_INBOUND.getCode());
        }
        order.setInboundAmount(newInboundAmount);
        order.setUpdateTime(LocalDateTime.now());
        purchaseOrderMapper.updateById(order);
    }

    private String generateInboundNo() {
        String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        return "PI" + ts + String.format("%04d", INBOUND_COUNTER.incrementAndGet());
    }
}
