package com.zioinfo.mes.production;

import com.zioinfo.mes.admin.AuditService;
import com.zioinfo.mes.bom.BomService;
import com.zioinfo.mes.inventory.InventoryService;
import com.zioinfo.mes.production.mapper.ProductionMapper;
import com.zioinfo.mes.workorder.MesWorkOrder;
import com.zioinfo.mes.workorder.mapper.WorkOrderMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

/**
 * 생산실적 서비스 — 작업지시 산출 보고.
 *
 * <p>실적 보고 시: 1) 산출 LOT 생성 2) BOM 소요 자재 투입(재고 차감 CONSUME)
 * 3) 산출 양품 입고(재고 증가 PRODUCE) 4) 작업지시 누적 산출/불량 갱신.
 * 작업지시는 RELEASED/INPROGRESS 상태에서만 실적 보고 가능(보고 시 INPROGRESS 자동 전이).
 */
@Service
@RequiredArgsConstructor
public class ProductionService {

    private final ProductionMapper mapper;
    private final WorkOrderMapper woMapper;
    private final BomService bomService;
    private final InventoryService inventory;
    private final AuditService audit;

    public List<MesProduction> list(String woNo, String itemCode, int limit) {
        return mapper.findAll(woNo, itemCode, limit <= 0 ? 200 : limit);
    }

    public MesProduction get(Long id) {
        MesProduction p = mapper.findById(id);
        if (p == null) throw new RuntimeException("ERR-PROD-404: 생산실적 없음");
        return p;
    }

    public List<Map<String, Object>> dailyOutput(int days) {
        return mapper.dailyOutput(days <= 0 ? 14 : days);
    }

    public List<Map<String, Object>> defectPareto(int days) {
        return mapper.defectPareto(days <= 0 ? 30 : days);
    }

    /**
     * 생산실적 보고 — LOT 생성 + 자재 투입 + 산출 입고 + 작업지시 누적.
     */
    @Transactional
    public MesProduction report(MesProduction p, String actor) {
        if (p.getWorkorderId() == null)
            throw new IllegalArgumentException("ERR-PROD-400: workorderId 필수");
        double good = p.getGoodQty() == null ? 0 : p.getGoodQty();
        double defect = p.getDefectQty() == null ? 0 : p.getDefectQty();
        if (good <= 0 && defect <= 0)
            throw new IllegalArgumentException("ERR-PROD-400: 양품 또는 불량 수량이 필요합니다");

        MesWorkOrder wo = woMapper.findById(p.getWorkorderId());
        if (wo == null) throw new RuntimeException("ERR-PROD-404: 작업지시 없음");
        if (!java.util.Set.of("RELEASED", "INPROGRESS").contains(wo.getStatus())) {
            throw new RuntimeException("ERR-PROD-409: RELEASED/INPROGRESS 작업지시만 실적 보고 가능(현재 " + wo.getStatus() + ")");
        }
        if ("RELEASED".equals(wo.getStatus())) {
            woMapper.updateStatus(wo.getId(), "INPROGRESS");
        }

        p.setWoNo(wo.getWoNo());
        p.setItemCode(wo.getItemCode());
        String wh = p.getWarehouseCode() != null ? p.getWarehouseCode() : wo.getWarehouseCode();
        p.setWarehouseCode(wh);
        if (p.getLotNo() == null || p.getLotNo().isBlank()) {
            p.setLotNo(generateLot(wo.getItemCode()));
        }
        p.setCreatedBy(actor);
        mapper.insert(p);

        double totalInput = good + defect;
        // 1) BOM 자재 투입(재고 차감) — 소요량 = 산출수량 기준. 재고 부족 시 트랜잭션 롤백.
        if (totalInput > 0) {
            Map<String, Double> req = bomService.materialRequirement(wo.getItemCode(), totalInput, null);
            for (Map.Entry<String, Double> e : req.entrySet()) {
                inventory.outbound("CONSUME", e.getKey(), wh, null, null, e.getValue(),
                    "WORKORDER", wo.getWoNo(), actor);
            }
        }
        // 2) 산출 양품 입고(재고 증가, 산출 LOT)
        if (good > 0) {
            inventory.inbound("PRODUCE", wo.getItemCode(), wh, null, p.getLotNo(), good,
                "WORKORDER", wo.getWoNo(), actor);
        }
        // 3) 작업지시 누적 산출/불량
        woMapper.addProduced(wo.getId(), good, defect);

        audit.log("PRODUCTION_REPORT", wo.getWoNo(),
            "good=" + good + " defect=" + defect + " lot=" + p.getLotNo());
        return mapper.findById(p.getId());
    }

    private String generateLot(String itemCode) {
        String prefix = itemCode == null ? "LOT" : itemCode.replaceAll("[^A-Za-z0-9]", "");
        if (prefix.length() > 8) prefix = prefix.substring(0, 8);
        return prefix + "-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
            + "-" + (System.currentTimeMillis() % 100000);
    }
}
