package kr.itn.itnhub.process;

import kr.itn.itnhub.org.OrgNotFoundException;
import kr.itn.itnhub.org.OrganizationMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class ProcessImportService {

    private final ProcessParser parser;
    private final ProcessItemMapper mapper;
    private final OrganizationMapper orgMapper;
    private final kr.itn.itnhub.stage.StageService stageService;
    private final kr.itn.itnhub.code.CodeService codeService;
    private final TransactionTemplate transactionTemplate;

    public ProcessImportService(ProcessParser parser, ProcessItemMapper mapper, OrganizationMapper orgMapper,
                                 kr.itn.itnhub.stage.StageService stageService,
                                 kr.itn.itnhub.code.CodeService codeService,
                                 PlatformTransactionManager transactionManager) {
        this.parser = parser;
        this.mapper = mapper;
        this.orgMapper = orgMapper;
        this.stageService = stageService;
        this.codeService = codeService;
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    /** 공공누리 유형의 옛 표기를 표준 표기로 맞춘다(제4유형 → 4유형). */
    private String canonicalKogl(String value) {
        return codeService.canonical(kr.itn.itnhub.code.Codes.KOGL_TYPE, value);
    }

    /**
     * 엑셀 파싱은 DB와 무관하므로 트랜잭션 밖에서 수행한다(ReviewImportService와 동일한 이유).
     */
    public ProcessImportReport importFile(Long orgId, InputStream xlsx) {
        if (orgMapper.findById(orgId) == null) {
            throw new OrgNotFoundException("기관을 찾을 수 없습니다: " + orgId);
        }
        List<ProcessRow> rows = parser.parse(xlsx);
        ProcessImportReport report = transactionTemplate.execute(status -> importRows(orgId, rows));

        // 요구사항 [18]: 권리처리 자료가 올라오면 ⑧법률검토(권리처리)로 저절로 넘어간다.
        // 이미 그보다 앞서 있으면 아무 일도 하지 않는다.
        if (report != null && report.total() > 0) {
            stageService.autoAdvance(orgId, kr.itn.itnhub.stage.AutoStage.PROCESS_STARTED,
                    kr.itn.itnhub.stage.AutoStage.REASON_PROCESS_STARTED);
        }
        return report;
    }

    private ProcessImportReport importRows(Long orgId, List<ProcessRow> rows) {
        // 이미 DB에 있는 순번 + 이번 파일에서 먼저 처리된 순번을 모두 "이미 존재함"으로 취급한다
        // (ReviewImportService.importRows와 동일한 관례).
        Set<Integer> seen = new HashSet<>(mapper.findSeqsByOrg(orgId));

        int created = 0;
        int updated = 0;
        for (ProcessRow row : rows) {
            if (seen.add(row.seq())) {
                created++;
            } else {
                updated++;
            }
            mapper.upsertFromFile(toProcessItem(orgId, row));
        }
        return new ProcessImportReport(created, updated, rows.size());
    }

    private ProcessItem toProcessItem(Long orgId, ProcessRow row) {
        ProcessItem item = new ProcessItem();
        item.setOrgId(orgId);
        item.setSeq(row.seq());
        item.setSiteName(row.siteName());
        item.setCategory(row.category());
        item.setBoardName(row.boardName());
        item.setPostTitle(row.postTitle());
        item.setUrl(row.url());
        item.setDescription(row.description());
        item.setHasAttachment(row.hasAttachment());
        // 옛 표기(제4유형)로 적혀 온 값은 표준 표기(4유형)로 맞춰 넣는다.
        item.setPriorKoglType(canonicalKogl(row.priorKoglType()));
        item.setContractDocs(row.contractDocs());
        item.setProducedDate(row.producedDate());
        item.setPublishedDate(row.publishedDate());
        item.setReviewMajor(row.reviewMajor());
        item.setReviewMinor(row.reviewMinor());
        item.setReviewResult(row.reviewResult());
        item.setReviewKoglType(canonicalKogl(row.reviewKoglType()));
        item.setReviewAiType(row.reviewAiType());
        item.setReviewOpinion(row.reviewOpinion());
        item.setReviewNote(row.reviewNote());
        item.setPriorEvidence(row.priorEvidence());
        item.setJudgedKoglType(canonicalKogl(row.judgedKoglType()));
        item.setJudgedAiType(row.judgedAiType());
        item.setFinalOpinion(row.finalOpinion());
        item.setJudgmentBasis(row.judgmentBasis());
        item.setProcessStatus(row.processStatus());
        return item;
    }
}
