package com.zioinfo.mes.job;

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

/**
 * 작업관리 서비스 — 작업 생성·작업자/설비 배정·할당·진행 전이·실적 누적·이력 조회.
 *
 * <p>상태 전이: ASSIGNED → STARTED → (PAUSED ↔ STARTED) → DONE (CANCELLED 가능).
 * 작업지시(workorder)를 참조해 itemCode/woNo 를 보정한다.
 */
@Service
@RequiredArgsConstructor
public class JobService {

    private static final Map<String, Set<String>> TRANSITIONS = Map.of(
        "ASSIGNED", Set.of("STARTED", "CANCELLED"),
        "STARTED", Set.of("PAUSED", "DONE"),
        "PAUSED", Set.of("STARTED", "CANCELLED"),
        "DONE", Set.of(),
        "CANCELLED", Set.of()
    );

    private final JobMapper mapper;
    private final WorkOrderMapper workOrderMapper;
    private final AuditService audit;

    public List<MesJob> list(String status, String workerId, String woNo, String equipmentCode) {
        return mapper.findAll(status, workerId, woNo, equipmentCode);
    }

    public MesJob get(Long id) {
        MesJob j = mapper.findById(id);
        if (j == null) throw new RuntimeException("ERR-JOB-404: 작업 없음");
        return j;
    }

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

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

    /** 작업 생성(작업지시 공정 할당). workorderId 로 woNo/itemCode 보정. */
    @Transactional
    public MesJob create(MesJob job, String actor) {
        if (job.getWorkorderId() == null)
            throw new IllegalArgumentException("ERR-JOB-400: workorderId 필수");
        MesWorkOrder wo = workOrderMapper.findById(job.getWorkorderId());
        if (wo == null) throw new RuntimeException("ERR-JOB-404: 작업지시 없음");
        job.setWoNo(wo.getWoNo());
        if (job.getItemCode() == null) job.setItemCode(wo.getItemCode());
        if (job.getAssignedQty() == null || job.getAssignedQty() <= 0)
            job.setAssignedQty(wo.getPlanQty());
        if (job.getJobNo() == null || job.getJobNo().isBlank())
            job.setJobNo("JOB-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
                + "-" + (System.currentTimeMillis() % 100000));
        if (mapper.countByNo(job.getJobNo()) > 0)
            throw new RuntimeException("ERR-JOB-409: 중복 작업번호");
        job.setStatus("ASSIGNED");
        job.setCreatedBy(actor);
        mapper.insert(job);
        audit.log("JOB_CREATE", job.getJobNo(),
            "wo=" + job.getWoNo() + " worker=" + job.getWorkerId() + " eqp=" + job.getEquipmentCode());
        return mapper.findById(job.getId());
    }

    /** 작업자/설비 재배정. */
    @Transactional
    public MesJob assign(Long id, String workerId, String equipmentCode, String actor) {
        MesJob j = get(id);
        if (Set.of("DONE", "CANCELLED").contains(j.getStatus()))
            throw new RuntimeException("ERR-JOB-409: 종료된 작업은 재배정 불가");
        mapper.assign(id, workerId, equipmentCode);
        audit.log("JOB_ASSIGN", j.getJobNo(), "worker=" + workerId + " eqp=" + equipmentCode);
        return mapper.findById(id);
    }

    /** 진행 상태 전이 — action: START/PAUSE/RESUME/DONE/CANCEL. */
    @Transactional
    public MesJob transition(Long id, String action, String actor) {
        MesJob j = get(id);
        String from = j.getStatus();
        String to = switch (action == null ? "" : action.toUpperCase()) {
            case "START", "RESUME" -> "STARTED";
            case "PAUSE" -> "PAUSED";
            case "DONE" -> "DONE";
            case "CANCEL" -> "CANCELLED";
            default -> throw new IllegalArgumentException("ERR-JOB-400: 알 수 없는 action(START/PAUSE/RESUME/DONE/CANCEL)");
        };
        if (!TRANSITIONS.getOrDefault(from, Set.of()).contains(to))
            throw new RuntimeException("ERR-JOB-409: " + from + " → " + to + " 전이 불가");
        mapper.updateStatus(id, to);
        audit.log("JOB_" + action.toUpperCase(), j.getJobNo(), from + " -> " + to);
        return mapper.findById(id);
    }

    /** 작업 실적 누적(완료 수량). */
    @Transactional
    public MesJob reportDone(Long id, double qty, String actor) {
        MesJob j = get(id);
        if (qty <= 0) throw new IllegalArgumentException("ERR-JOB-400: 수량은 0보다 커야 합니다");
        mapper.addDone(id, qty);
        audit.log("JOB_DONE_QTY", j.getJobNo(), "+" + qty);
        return mapper.findById(id);
    }

    @Transactional
    public MesJob update(Long id, MesJob job, String actor) {
        MesJob cur = get(id);
        if (Set.of("DONE", "CANCELLED").contains(cur.getStatus()))
            throw new RuntimeException("ERR-JOB-409: 종료된 작업은 수정 불가");
        job.setId(id);
        if (job.getAssignedQty() == null) job.setAssignedQty(cur.getAssignedQty());
        mapper.update(job);
        audit.log("JOB_UPDATE", cur.getJobNo(), "id=" + id);
        return mapper.findById(id);
    }

    public void delete(Long id) {
        MesJob cur = get(id);
        if (!Set.of("ASSIGNED", "CANCELLED").contains(cur.getStatus()))
            throw new RuntimeException("ERR-JOB-409: 진행/완료 작업은 삭제 불가");
        mapper.delete(id);
        audit.log("JOB_DELETE", cur.getJobNo(), "");
    }
}
