package kr.itn.itnhub.seed;

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;

/**
 * 신유형 신청목록 시트의 모든 행을 개별 기관으로 읽는다.
 *
 * 시트 배치 (1행 그룹제목, 2행 헤더, 3행부터 데이터):
 *   B=연번1  C=연번2  F=기관명  G=부서명  H=담당자명  I=직급/직함  J=유선전화  K=이메일
 *
 * 기관 코드는 {연번1}_{연번2} 합성 코드다(예: 001_00, 008_02). 연번1+연번2+기관명이
 * 모두 있는 행은 전부 별도 기관이 된다 - 예전처럼 연번2="00" 대표기관에 하위기관을
 * 흡수하지 않는다(경찰청_치안정책연구소 같은 예외도 더 이상 필요 없다 - 연번 자체가
 * 하위기관까지 구분하기 때문). 담당자 정보가 없는 행은 담당자 필드만 null이 되고
 * 행 자체는 그대로 만들어진다.
 */
@Component
public class SeedParser {

    public static final String SHEET_NAME = "신유형 신청목록";

    private static final int DATA_START_ROW_INDEX = 2; // 3행
    private static final int COL_ORG_NO = 1;    // B
    private static final int COL_SUB_NO = 2;    // C
    private static final int COL_ORG_NAME = 5;  // F
    private static final int COL_DEPT = 6;      // G
    private static final int COL_MANAGER = 7;   // H
    private static final int COL_TITLE = 8;     // I
    private static final int COL_PHONE = 9;     // J
    private static final int COL_EMAIL = 10;    // K

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

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

        for (int i = DATA_START_ROW_INDEX; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) {
                continue;
            }

            String orgNo1 = orgNo1(row);
            String subNo = subNo(row);
            String orgName = string(row, COL_ORG_NAME);
            if (orgNo1.isBlank() || subNo.isBlank() || orgName.isBlank()) {
                continue;
            }

            String orgNo = orgNo1 + "_" + subNo;

            result.add(new SeedRow(
                    orgNo,
                    orgName,
                    orgNo,
                    nullIfBlank(string(row, COL_DEPT)),
                    nullIfBlank(string(row, COL_MANAGER)),
                    nullIfBlank(string(row, COL_TITLE)),
                    nullIfBlank(string(row, COL_PHONE)),
                    nullIfBlank(string(row, COL_EMAIL))));
        }
        return result;
    }

    /** 연번1은 항상 3자리 문자열이다. 숫자로 저장된 셀도 앞자리 0을 복원해 준다. */
    private String orgNo1(Row row) {
        String raw = string(row, COL_ORG_NO);
        return raw.isBlank() ? "" : pad(raw, 3);
    }

    /** 연번2도 항상 2자리 문자열이다("0" → "00", "1" → "01"). */
    private String subNo(Row row) {
        String raw = string(row, COL_SUB_NO);
        return raw.isBlank() ? "" : pad(raw, 2);
    }

    private String pad(String value, int width) {
        String v = value.trim();
        return v.length() >= width ? v : "0".repeat(width - v.length()) + v;
    }

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

    /**
     * cellType으로 셀 값을 문자열로 변환한다. FORMULA는 수식을 평가하지 않고
     * 파일에 저장된 캐시 결과 타입({@link Cell#getCachedFormulaResultType()})으로 재귀 위임해
     * STRING/NUMERIC/BOOLEAN 처리 로직을 그대로 재사용한다.
     */
    private String cellValue(Cell cell, CellType cellType) {
        return switch (cellType) {
            case STRING -> cell.getStringCellValue().trim();
            // 반올림: 부동소수점 오차로 6.999999999999998처럼 저장된 값이 절삭되어
            // 한 자리 작은 값("006")으로 읽히는 것을 방지한다.
            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;
    }
}
