package com.zioinfo.mes.workorder;

import com.zioinfo.mes.admin.AuditService;
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;
import java.util.Set;

/**
 * 작업지시 서비스 — 발번·상태 전이(CREATED→RELEASED→INPROGRESS→DONE→CLOSED)·실적 누적.
 *
 * <p>상태 전이는 허용 경로만 가능. RELEASE 이상은 Manager+ 가드(컨트롤러).
 */
@Service
@RequiredArgsConstructor
public class WorkOrderService {

    private static final Map<String, Set<String>> TRANSITIONS = Map.of(
        "CREATED", Set.of("RELEASED", "CANCELLED"),
        "RELEASED", Set.of("INPROGRESS", "CANCELLED"),
        "INPROGRESS", Set.of("DONE"),
        "DONE", Set.of("CLOSED", "INPROGRESS"),
        "CLOSED", Set.of(),
        "CANCELLED", Set.of()
    );

    private final WorkOrderMapper mapper;
    private final AuditService audit;

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

    public MesWorkOrder get(Long id) {
        MesWorkOrder wo = mapper.findById(id);
        if (wo == null) throw new RuntimeException("ERR-WO-404: 작업지시 없음");
        return wo;
    }

    public List<Map<String, Object>> kanbanCounts() {
        return mapper.countByStatus();
    }

    @Transactional
    public MesWorkOrder create(MesWorkOrder wo, String actor) {
        if (wo.getItemCode() == null || wo.getItemCode().isBlank())
            throw new IllegalArgumentException("ERR-WO-400: itemCode 필수");
        if (wo.getPlanQty() == null || wo.getPlanQty() <= 0)
            throw new IllegalArgumentException("ERR-WO-400: planQty 는 0보다 커야 합니다");
        if (wo.getWoNo() == null || wo.getWoNo().isBlank()) {
            wo.setWoNo(generateNo());
        }
        if (mapper.countByNo(wo.getWoNo()) > 0)
            throw new RuntimeException("ERR-WO-409: 중복 작업지시번호");
        wo.setStatus("CREATED");
        wo.setProducedQty(0.0);
        wo.setDefectQty(0.0);
        if (wo.getPriority() == null) wo.setPriority("MEDIUM");
        wo.setCreatedBy(actor);
        mapper.insert(wo);
        audit.log("WORKORDER_CREATE", wo.getWoNo(), "item=" + wo.getItemCode() + " qty=" + wo.getPlanQty());
        return mapper.findById(wo.getId());
    }

    @Transactional
    public MesWorkOrder transition(Long id, String action, String actor) {
        MesWorkOrder wo = get(id);
        String from = wo.getStatus();
        String to = switch (action == null ? "" : action.toUpperCase()) {
            case "RELEASE" -> "RELEASED";
            case "START" -> "INPROGRESS";
            case "COMPLETE" -> "DONE";
            case "CLOSE" -> "CLOSED";
            case "CANCEL" -> "CANCELLED";
            default -> throw new IllegalArgumentException("ERR-WO-400: 알 수 없는 action(RELEASE/START/COMPLETE/CLOSE/CANCEL)");
        };
        if (!TRANSITIONS.getOrDefault(from, Set.of()).contains(to)) {
            throw new RuntimeException("ERR-WO-409: " + from + " → " + to + " 전이 불가");
        }
        mapper.updateStatus(id, to);
        audit.log("WORKORDER_" + action.toUpperCase(), wo.getWoNo(), from + " -> " + to);
        return mapper.findById(id);
    }

    public MesWorkOrder update(Long id, MesWorkOrder wo, String actor) {
        MesWorkOrder cur = get(id);
        if (!"CREATED".equals(cur.getStatus())) {
            throw new RuntimeException("ERR-WO-409: CREATED 상태에서만 수정 가능");
        }
        wo.setId(id);
        wo.setWoNo(cur.getWoNo());
        wo.setStatus(cur.getStatus());
        mapper.update(wo);
        audit.log("WORKORDER_UPDATE", cur.getWoNo(), "id=" + id);
        return mapper.findById(id);
    }

    public void delete(Long id) {
        MesWorkOrder cur = get(id);
        if (!Set.of("CREATED", "CANCELLED").contains(cur.getStatus())) {
            throw new RuntimeException("ERR-WO-409: 진행/완료 작업지시는 삭제 불가");
        }
        mapper.delete(id);
        audit.log("WORKORDER_DELETE", cur.getWoNo(), "");
    }

    private String generateNo() {
        return "WO-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
            + "-" + (System.currentTimeMillis() % 100000);
    }
}
