package kr.itn.itnhub.process;

import kr.itn.itnhub.AbstractDbTest;
import kr.itn.itnhub.org.Organization;
import kr.itn.itnhub.org.OrganizationMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import java.io.ByteArrayInputStream;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * 권리처리 엑셀의 업로드↔다운로드 왕복.
 *
 * <p>업로드는 컬럼 번호({@code ProcessParser.COL_*})로, 다운로드는 헤더 이름
 * ({@code ProcessService.COLUMN_HEADERS})으로 각각 따로 정의돼 있다. 두 정의가 어긋나면
 * "내려받아 고쳐서 다시 올리는" 흐름에서 값이 조용히 사라진다. 그 어긋남을 여기서 잡는다.</p>
 */
class ProcessExcelRoundTripTest extends AbstractDbTest {

    @Autowired
    ProcessService service;

    @Autowired
    ProcessParser parser;

    @Autowired
    OrganizationMapper orgMapper;

    @Autowired
    JdbcTemplate jdbc;

    private Long orgId;

    @BeforeEach
    void setUp() {
        jdbc.update("delete from process_item");
        jdbc.update("delete from organization where org_no = '920'");

        Organization org = new Organization();
        org.setOrgNo("920");
        org.setOrgName("왕복테스트기관");
        org.setChannelSlug("920");
        orgMapper.upsertBySeed(org);
        orgId = orgMapper.findByOrgNoAndOrgName("920", "왕복테스트기관").getId();
    }

    @AfterEach
    void cleanUp() {
        jdbc.update("delete from process_item");
        jdbc.update("delete from organization where org_no = '920'");
    }

    /** 헤더 이름과 파서가 읽는 컬럼이 같은 자리를 가리키는지. 한쪽만 고치면 여기서 깨진다. */
    @Test
    void 헤더_이름과_파서_컬럼이_같은_자리를_가리킨다() {
        assertHeader(ProcessParser.COL_SEQ, "순번");
        assertHeader(ProcessParser.COL_SITE_NAME, "사이트명");
        assertHeader(ProcessParser.COL_CATEGORY, "범주");
        assertHeader(ProcessParser.COL_BOARD_NAME, "게시판명");
        assertHeader(ProcessParser.COL_POST_TITLE, "게시물제목");
        assertHeader(ProcessParser.COL_URL, "URL주소");
        assertHeader(ProcessParser.COL_DESCRIPTION, "설명");
        assertHeader(ProcessParser.COL_HAS_ATTACHMENT, "첨부파일여부");
        assertHeader(ProcessParser.COL_PRIOR_KOGL_TYPE, "기존 공공누리유형(+AI)");
        assertHeader(ProcessParser.COL_CONTRACT_DOCS, "계약서 유무");
        assertHeader(ProcessParser.COL_PRODUCED_DATE, "제작일");
        assertHeader(ProcessParser.COL_PUBLISHED_DATE, "공표일");
        assertHeader(ProcessParser.COL_REVIEW_MAJOR, "권리확인(대분류)");
        assertHeader(ProcessParser.COL_REVIEW_MINOR, "권리확인(세부)");
        assertHeader(ProcessParser.COL_REVIEW_RESULT, "처리 구분");
        assertHeader(ProcessParser.COL_REVIEW_KOGL_TYPE, "공공누리 유형");
        assertHeader(ProcessParser.COL_REVIEW_AI_TYPE, "AI유형");
        assertHeader(ProcessParser.COL_REVIEW_OPINION, "의견");
        assertHeader(ProcessParser.COL_REVIEW_NOTE, "비고");
        assertHeader(ProcessParser.COL_JUDGED_KOGL_TYPE, "공공누리유형(판정)");
        assertHeader(ProcessParser.COL_JUDGED_AI_TYPE, "AI유형(판정)");
        assertHeader(ProcessParser.COL_FINAL_OPINION, "최종의견");
        assertHeader(ProcessParser.COL_JUDGMENT_BASIS, "판단근거");
        assertHeader(ProcessParser.COL_PROCESS_STATUS, "처리상태");
    }

    private void assertHeader(int column, String expected) {
        assertThat(ProcessService.COLUMN_HEADERS[column])
                .as("%d번 열의 헤더", column)
                .isEqualTo(expected);
    }

    /**
     * 기존 증빙자료는 예외다. 기관이 보내오는 실제 파일은 헤더가 T열인데 데이터가 U열에 있어
     * 파서가 U를 먼저 읽고, 비어 있으면 헤더 자리인 T를 읽는다. 이 어긋남 자체를 명시적으로
     * 남겨 두어, 나중에 누가 "헤더랑 다르네" 하고 한쪽만 고치다 깨뜨리지 않게 한다.
     */
    @Test
    void 기존_증빙자료만_헤더와_데이터_열이_다르다() {
        assertThat(ProcessService.COLUMN_HEADERS[19]).isEqualTo("기존 증빙자료");
        assertThat(ProcessParser.COL_PRIOR_EVIDENCE_HEADER).isEqualTo(19);
        assertThat(ProcessParser.COL_PRIOR_EVIDENCE).isEqualTo(20);
    }

    /** 기관 원본처럼 U열에 값이 있는 파일도 그대로 읽어야 한다(다운로드 파일만 고치면 안 된다). */
    @Test
    void 원본처럼_U열에_값이_있어도_읽는다() throws Exception {
        byte[] xlsx = ProcessParserFixtures.withPriorEvidenceAt(ProcessParser.COL_PRIOR_EVIDENCE, "원본증빙");

        List<ProcessRow> rows = parser.parse(new ByteArrayInputStream(xlsx));

        assertThat(rows).hasSize(1);
        assertThat(rows.get(0).priorEvidence()).isEqualTo("원본증빙");
    }

    /** 내려받은 파일을 그대로 다시 올렸을 때 값이 살아남아야 한다. */
    @Test
    void 내려받아_다시_올려도_값이_그대로다() {
        jdbc.update("insert into process_item (org_id, seq, site_name, category, board_name, "
                        + "post_title, url, description, has_attachment, prior_kogl_type, contract_docs, "
                        + "produced_date, published_date, review_major, review_minor, review_result, "
                        + "review_kogl_type, review_ai_type, review_opinion, review_note, prior_evidence, "
                        + "judged_kogl_type, judged_ai_type, final_opinion, judgment_basis, process_status) "
                        + "values (?, 1, '사이트', '범주', '게시판', '제목', 'http://x', '설명', '있음', "
                        + "'4유형', '양도계약서', '2026-01-02', '2026-01-03', '제3자 권리', '초상권 포함', "
                        + "'권리처리 추진', '보류', 'Y', '의견', '비고', '증빙자료', '0유형', 'Y', "
                        + "'최종의견', '판단근거', '처리완료')",
                orgId);

        byte[] xlsx = service.downloadWorkbook(orgId);
        List<ProcessRow> rows = parser.parse(new ByteArrayInputStream(xlsx));

        assertThat(rows).hasSize(1);
        ProcessRow row = rows.get(0);

        assertThat(row.siteName()).isEqualTo("사이트");
        assertThat(row.category()).isEqualTo("범주");
        assertThat(row.boardName()).isEqualTo("게시판");
        assertThat(row.postTitle()).isEqualTo("제목");
        assertThat(row.url()).isEqualTo("http://x");
        assertThat(row.description()).isEqualTo("설명");
        assertThat(row.hasAttachment()).isEqualTo("있음");
        assertThat(row.priorKoglType()).isEqualTo("4유형");
        assertThat(row.contractDocs()).isEqualTo("양도계약서");
        assertThat(row.producedDate()).isEqualTo("2026-01-02");
        assertThat(row.publishedDate()).isEqualTo("2026-01-03");
        assertThat(row.reviewMajor()).isEqualTo("제3자 권리");
        assertThat(row.reviewMinor()).isEqualTo("초상권 포함");
        assertThat(row.reviewResult()).isEqualTo("권리처리 추진");
        assertThat(row.reviewKoglType()).isEqualTo("보류");
        assertThat(row.reviewAiType()).isEqualTo("Y");
        assertThat(row.reviewOpinion()).isEqualTo("의견");
        assertThat(row.reviewNote()).isEqualTo("비고");
        assertThat(row.judgedKoglType()).isEqualTo("0유형");
        assertThat(row.judgedAiType()).isEqualTo("Y");
        assertThat(row.finalOpinion()).isEqualTo("최종의견");
        assertThat(row.judgmentBasis()).isEqualTo("판단근거");
        assertThat(row.processStatus()).isEqualTo("처리완료");

        // 기존 증빙자료는 다운로드가 T열에 쓰고 파서가 U열을 읽어 왕복에서 사라진다.
        assertThat(row.priorEvidence())
                .as("내려받아 다시 올렸을 때 기존 증빙자료")
                .isEqualTo("증빙자료");
    }
}
