package kr.itn.itnhub.dashboard;

import kr.itn.itnhub.code.CodeService;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 대시보드 화면 하나가 쓰는 서비스. 집계 쿼리는 {@link DashboardMapper#findOrgRows()} 한 번이고,
 * 상단 KPI는 그 결과를 Java에서 접어 만든다 - 같은 원본에서 계산해야 타일 숫자와 아래 표/칸반
 * 숫자가 어긋나지 않는다(쿼리를 따로 두면 두 결과 사이에 시차가 생긴다).
 *
 * <p>어느 단계부터 각 KPI에 세는지는 코드표(STAGE의 kpiFrom 속성)에서 온다. 예전에는 여기
 * 상수 세 개로 박혀 있어서, 기준이 바뀔 때마다 배포가 필요했다.</p>
 */
@Service
public class DashboardService {

    /** KPI 이름. 코드표의 kpiFrom 값과 맞물린다. */
    static final String KPI_DOC_SUBMITTED = "docSubmitted";
    static final String KPI_COMPLETED = "completed";
    static final String KPI_REPORT = "reportWriting";

    private final DashboardMapper mapper;
    private final CodeService codeService;

    public DashboardService(DashboardMapper mapper, CodeService codeService) {
        this.mapper = mapper;
        this.codeService = codeService;
    }

    /**
     * 이 KPI에 집계하기 시작하는 단계 번호. 코드표에 지정이 없으면 아무 기관도 세지 않도록
     * {@link Integer#MAX_VALUE}를 준다 - 0을 주면 단계가 있는 모든 기관이 잘못 집계된다.
     */
    private int thresholdOf(String kpi) {
        return codeService.group(kr.itn.itnhub.code.Codes.STAGE).stream()
                .filter(code -> kpi.equals(code.attr(kr.itn.itnhub.code.Codes.ATTR_KPI_FROM)))
                .mapToInt(code -> Integer.parseInt(code.code()))
                .min()
                .orElse(Integer.MAX_VALUE);
    }

    public DashboardResponse dashboard() {
        List<DashboardOrgRow> rows = mapper.findOrgRows(doneStatus());
        return new DashboardResponse(summarize(rows), rows);
    }

    /**
     * 권리처리 완료로 셀 상태값. 지정이 없으면 어떤 행도 완료로 세지 않도록 매칭되지 않는
     * 값을 준다 - 빈 문자열을 주면 처리상태가 비어 있는 행이 전부 완료로 잡힌다.
     */
    private String doneStatus() {
        return codeService.group(kr.itn.itnhub.code.Codes.PROCESS_STATUS).stream()
                .filter(code -> code.flag(kr.itn.itnhub.code.Codes.ATTR_DONE))
                .map(kr.itn.itnhub.code.Code::code)
                .findFirst()
                .orElse(" ");
    }

    DashboardSummary summarize(List<DashboardOrgRow> rows) {
        int docSubmittedFrom = thresholdOf(KPI_DOC_SUBMITTED);
        int completedFrom = thresholdOf(KPI_COMPLETED);
        int reportFrom = thresholdOf(KPI_REPORT);

        int docSubmitted = 0;
        int reviewTotal = 0;
        int reviewDone = 0;
        int processTotal = 0;
        int processDone = 0;
        int unresolvedRe = 0;
        int reportWriting = 0;
        int completed = 0;

        for (DashboardOrgRow row : rows) {
            reviewTotal += row.reviewTotal();
            reviewDone += row.reviewDone();
            processTotal += row.processTotal();
            processDone += row.processDone();
            unresolvedRe += row.reCount();

            Integer stage = row.stage();
            if (stage == null) {
                continue;
            }
            if (stage >= docSubmittedFrom) {
                docSubmitted++;
            }
            if (stage >= completedFrom) {
                completed++;
            }
            if (stage >= reportFrom) {
                reportWriting++;
            }
        }

        // 신청 기관 수는 명부에 등록된 기관 전체다 - 명부에 올라온 것 자체가 신청이므로
        // 단계값 유무와 무관하다(채널 생성 전 기관도 신청은 한 상태다).
        return new DashboardSummary(rows.size(), docSubmitted, reviewTotal, reviewDone,
                processTotal, processDone, unresolvedRe, reportWriting, completed);
    }
}
