package kr.itn.itnhub.review;

import kr.itn.itnhub.seed.SeedParseException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 기관별 권리확인 엑셀(시트명 {@code Sheet0}, 없으면 첫 시트로 대체)을 읽는다.
 *
 * 시트 배치 (1행 그룹제목, 2행 헤더, 3행부터 데이터, A열(순번)이 빈 첫 행에서 데이터 끝):
 *   A 순번  B 사이트명  C 범주  D 게시판 경로  E 게시판명  F 게시물제목  G URL주소
 *   H 게시글 등록  I 제작일  J 공표일  K 첨부파일여부  L 기존 공공누리부착  M 기존 공공누리유형
 *   N AI유형  O 비고(조사원)  P 개방가능여부  Q 권리확인(대분류)  R 권리확인(세부)  S 처리결과
 *   T 공공누리유형(판정)  U AI유형(판정)  V 의견  W (빈 열)  X 비고(변호사)  Y 권리처리필요
 *
 * 시트의 선언된 dimension은 서식만 채워진 40만 행 안팎이라, lastRowNum까지 무조건 돌되
 * A열이 비면 즉시 멈춘다(전체 dimension을 다 읽지 않는다).
 *
 * 제작일/공표일 셀은 날짜서식이 입혀진 숫자({@code 20230904})라도 절대 날짜로 읽지 않고
 * 문자열 그대로 저장한다 - {@link #cellValue}가 NUMERIC을 항상 반올림 문자열로 바꾸고
 * {@code getDateCellValue()}를 호출하지 않으므로 자동으로 이 요구사항을 만족한다.
 * 수식 오류(#VALUE! 등) 셀은 캐시된 결과 타입이 ERROR가 되어 default 분기(빈 문자열 → null)로
 * 처리된다.
 */
@Component
public class ReviewParser {

    public static final String PRIMARY_SHEET_NAME = "Sheet0";

    private static final int DATA_START_ROW_INDEX = 2; // 3행

    private static final int COL_SEQ = 0;              // A
    private static final int COL_SITE_NAME = 1;        // B
    private static final int COL_CATEGORY = 2;         // C
    private static final int COL_BOARD_PATH = 3;       // D
    private static final int COL_BOARD_NAME = 4;       // E
    private static final int COL_POST_TITLE = 5;       // F
    private static final int COL_URL = 6;               // G
    private static final int COL_POST_REGISTERED = 7;  // H
    private static final int COL_PRODUCED_DATE = 8;    // I
    private static final int COL_PUBLISHED_DATE = 9;   // J
    private static final int COL_HAS_ATTACHMENT = 10;  // K
    private static final int COL_KOGL_ATTACHED = 11;   // L
    private static final int COL_KOGL_TYPE = 12;       // M
    private static final int COL_AI_TYPE = 13;         // N
    private static final int COL_SURVEYOR_NOTE = 14;   // O
    private static final int COL_OPENABLE = 15;        // P
    private static final int COL_REVIEW_MAJOR = 16;    // Q
    private static final int COL_REVIEW_MINOR = 17;    // R
    private static final int COL_REVIEW_RESULT = 18;   // S
    private static final int COL_JUDGED_KOGL_TYPE = 19; // T
    private static final int COL_JUDGED_AI_TYPE = 20;  // U
    private static final int COL_OPINION = 21;         // V
    // 22 = W, 빈 열
    private static final int COL_LAWYER_NOTE = 23;     // X
    private static final int COL_NEEDS_PROCESSING = 24; // Y

    public List<ReviewRow> parse(InputStream xlsx) {
        try (Workbook wb = WorkbookFactory.create(xlsx)) {
            Sheet sheet = wb.getSheet(PRIMARY_SHEET_NAME);
            if (sheet == null && wb.getNumberOfSheets() > 0) {
                sheet = wb.getSheetAt(0);
            }
            if (sheet == null) {
                throw new SeedParseException("통합문서에 읽을 수 있는 시트가 없습니다.");
            }
            return readRows(sheet);
        } catch (IOException e) {
            throw new SeedParseException("엑셀 파일을 읽지 못했습니다.", e);
        }
    }

    private List<ReviewRow> readRows(Sheet sheet) {
        List<ReviewRow> result = new ArrayList<>();

        for (int i = DATA_START_ROW_INDEX; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) {
                break; // 데이터 영역은 연속적이다 - 빈 행이 나오면 그게 끝이다
            }

            String seqRaw = string(row, COL_SEQ);
            if (seqRaw.isBlank()) {
                break;
            }
            int seq;
            try {
                seq = Integer.parseInt(seqRaw.trim());
            } catch (NumberFormatException e) {
                break;
            }

            result.add(new ReviewRow(
                    seq,
                    nullIfBlank(string(row, COL_SITE_NAME)),
                    nullIfBlank(string(row, COL_CATEGORY)),
                    nullIfBlank(string(row, COL_BOARD_PATH)),
                    nullIfBlank(string(row, COL_BOARD_NAME)),
                    nullIfBlank(string(row, COL_POST_TITLE)),
                    nullIfBlank(string(row, COL_URL)),
                    nullIfBlank(string(row, COL_POST_REGISTERED)),
                    nullIfBlank(string(row, COL_PRODUCED_DATE)),
                    nullIfBlank(string(row, COL_PUBLISHED_DATE)),
                    nullIfBlank(string(row, COL_HAS_ATTACHMENT)),
                    nullIfBlank(string(row, COL_KOGL_ATTACHED)),
                    nullIfBlank(string(row, COL_KOGL_TYPE)),
                    nullIfBlank(string(row, COL_AI_TYPE)),
                    nullIfBlank(string(row, COL_SURVEYOR_NOTE)),
                    nullIfBlank(string(row, COL_OPENABLE)),
                    nullIfBlank(string(row, COL_REVIEW_MAJOR)),
                    nullIfBlank(string(row, COL_REVIEW_MINOR)),
                    nullIfBlank(string(row, COL_REVIEW_RESULT)),
                    nullIfBlank(string(row, COL_JUDGED_KOGL_TYPE)),
                    nullIfBlank(string(row, COL_JUDGED_AI_TYPE)),
                    nullIfBlank(string(row, COL_OPINION)),
                    nullIfBlank(string(row, COL_LAWYER_NOTE)),
                    nullIfBlank(string(row, COL_NEEDS_PROCESSING))));
        }
        return result;
    }

    private String string(Row row, int columnIndex) {
        Cell cell = row.getCell(columnIndex);
        if (cell == null) {
            return "";
        }
        return cellValue(cell, cell.getCellType());
    }

    /** SeedParser/MemberDirectoryParser와 동일한 셀 값 변환 규칙. */
    private String cellValue(Cell cell, CellType cellType) {
        return switch (cellType) {
            case STRING -> cell.getStringCellValue().trim();
            case NUMERIC -> String.valueOf(Math.round(cell.getNumericCellValue()));
            case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
            case FORMULA -> cellValue(cell, cell.getCachedFormulaResultType());
            default -> "";
        };
    }

    private String nullIfBlank(String value) {
        return (value == null || value.isBlank()) ? null : value;
    }
}
