package com.zioinfo.mes.inventory;

import com.zioinfo.mes.admin.AuditService;
import com.zioinfo.mes.inventory.mapper.InventoryMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 재고 서비스 — 수불(입고/출고/투입/산출/이동/조정) 원장 + 잔고 갱신.
 *
 * <p>모든 수량 변동은 {@link MesInventoryTxn} 이력 + 잔고({@link MesInventory}) 동시 갱신(트랜잭션).
 * WMS(receiving/shipping/stocktake)·MES(production)·tracking 이 공용으로 호출한다.
 */
@Service
@RequiredArgsConstructor
public class InventoryService {

    private final InventoryMapper mapper;
    private final AuditService audit;

    public List<MesInventory> list(String itemCode, String warehouseCode, String keyword) {
        return mapper.findAll(itemCode, warehouseCode, keyword);
    }

    public Double available(String itemCode) {
        Double q = mapper.sumQty(itemCode);
        return q == null ? 0.0 : q;
    }

    public List<MesInventory> belowSafety() {
        return mapper.findBelowSafety();
    }

    public List<MesInventoryTxn> txns(String itemCode, String refType, String refNo, int limit) {
        return mapper.findTxns(itemCode, refType, refNo, limit <= 0 ? 200 : limit);
    }

    /** 입고/산출 — 잔고 증가 + 이력. */
    @Transactional
    public void inbound(String txnType, String itemCode, String warehouseCode, String locationCode,
                        String lotNo, double qty, String refType, String refNo, String actor) {
        if (qty <= 0) throw new IllegalArgumentException("ERR-INV-400: 수량은 0보다 커야 합니다");
        applyDelta(itemCode, warehouseCode, locationCode, lotNo, qty);
        recordTxn(txnType, itemCode, null, null, warehouseCode, locationCode, lotNo, qty, refType, refNo, actor);
    }

    /** 출고/투입 — 잔고 감소 + 이력. 음수 재고 차단. */
    @Transactional
    public void outbound(String txnType, String itemCode, String warehouseCode, String locationCode,
                         String lotNo, double qty, String refType, String refNo, String actor) {
        if (qty <= 0) throw new IllegalArgumentException("ERR-INV-400: 수량은 0보다 커야 합니다");
        MesInventory inv = mapper.findOne(itemCode, warehouseCode, locationCode, lotNo);
        double cur = inv == null || inv.getQty() == null ? 0 : inv.getQty();
        if (cur < qty) {
            throw new RuntimeException("ERR-INV-409: 재고 부족(" + itemCode + " 보유 " + cur + " < 요청 " + qty + ")");
        }
        applyDelta(itemCode, warehouseCode, locationCode, lotNo, -qty);
        recordTxn(txnType, itemCode, warehouseCode, locationCode, null, null, lotNo, qty, refType, refNo, actor);
    }

    /** 재고 이동(TRANSFER) — from 감소, to 증가, 단일 이력. */
    @Transactional
    public void transfer(String itemCode, String fromWh, String fromLoc, String toWh, String toLoc,
                         String lotNo, double qty, String actor) {
        if (qty <= 0) throw new IllegalArgumentException("ERR-INV-400: 수량은 0보다 커야 합니다");
        MesInventory src = mapper.findOne(itemCode, fromWh, fromLoc, lotNo);
        double cur = src == null || src.getQty() == null ? 0 : src.getQty();
        if (cur < qty) throw new RuntimeException("ERR-INV-409: 이동 재고 부족");
        applyDelta(itemCode, fromWh, fromLoc, lotNo, -qty);
        applyDelta(itemCode, toWh, toLoc, lotNo, qty);
        MesInventoryTxn txn = new MesInventoryTxn();
        txn.setTxnType("TRANSFER");
        txn.setItemCode(itemCode);
        txn.setFromWarehouse(fromWh); txn.setFromLocation(fromLoc);
        txn.setToWarehouse(toWh); txn.setToLocation(toLoc);
        txn.setLotNo(lotNo); txn.setQty(qty);
        txn.setRefType("TRANSFER"); txn.setCreatedBy(actor);
        mapper.insertTxn(txn);
        audit.log("INV_TRANSFER", itemCode, fromWh + "->" + toWh + " qty=" + qty);
    }

    /** 재고 실사 조정(ADJUST) — 목표 수량으로 보정. */
    @Transactional
    public void adjust(String itemCode, String warehouseCode, String locationCode, String lotNo,
                       double countedQty, String refNo, String actor) {
        MesInventory inv = mapper.findOne(itemCode, warehouseCode, locationCode, lotNo);
        double cur = inv == null || inv.getQty() == null ? 0 : inv.getQty();
        double delta = countedQty - cur;
        applyDelta(itemCode, warehouseCode, locationCode, lotNo, delta);
        recordTxn("ADJUST", itemCode, warehouseCode, locationCode, warehouseCode, locationCode,
            lotNo, delta, "STOCKTAKE", refNo, actor);
        audit.log("INV_ADJUST", itemCode, "delta=" + delta);
    }

    /** 잔고 증감 — 행 없으면 생성. */
    private void applyDelta(String itemCode, String warehouseCode, String locationCode, String lotNo, double delta) {
        MesInventory inv = mapper.findOne(itemCode, warehouseCode, locationCode, lotNo);
        if (inv == null) {
            inv = new MesInventory();
            inv.setItemCode(itemCode);
            inv.setWarehouseCode(warehouseCode);
            inv.setLocationCode(locationCode);
            inv.setLotNo(lotNo);
            inv.setQty(Math.max(0, delta));
            mapper.insertStock(inv);
        } else {
            double next = (inv.getQty() == null ? 0 : inv.getQty()) + delta;
            mapper.updateQty(inv.getId(), Math.max(0, next));
        }
    }

    private void recordTxn(String type, String itemCode, String fromWh, String fromLoc,
                           String toWh, String toLoc, String lotNo, double qty,
                           String refType, String refNo, String actor) {
        MesInventoryTxn txn = new MesInventoryTxn();
        txn.setTxnType(type);
        txn.setItemCode(itemCode);
        txn.setFromWarehouse(fromWh); txn.setFromLocation(fromLoc);
        txn.setToWarehouse(toWh); txn.setToLocation(toLoc);
        txn.setLotNo(lotNo); txn.setQty(qty);
        txn.setRefType(refType); txn.setRefNo(refNo);
        txn.setCreatedBy(actor);
        mapper.insertTxn(txn);
    }
}
