package incheon.ags.pss.batch;

import incheon.ags.dss.util.AbstractFileLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * sts_wind.txt (바람통계) 파일 로더.
 * AbstractFileLoader의 구현체.
 */
@Profile("data-load-pss")
@Component
@Order(3) // 실행 순서 지정
public class WindStatsLoader extends AbstractFileLoader {

    private static final Logger log = LoggerFactory.getLogger(WindStatsLoader.class);

    // 테이블 명세에 맞춘 INSERT / UPSERT SQL
    private static final String INSERT_SQL =
            "INSERT INTO icext.sm_wind_stats (" +
            "  tm, stn_id, lat, lon, altd, ws_mavg, ws_ins_max, ocdt_ws_ins_max, " +
            "  wd_ins_max, ws_max, ocdt_ws_max, wd_max, wd_frq, ws_mix, wd_mix, geom" +
            ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText(?, 4326)) " +
            "ON CONFLICT (tm, stn_id) DO UPDATE SET " +
            "  lat = EXCLUDED.lat, lon = EXCLUDED.lon, altd = EXCLUDED.altd, " +
            "  ws_mavg = EXCLUDED.ws_mavg, ws_ins_max = EXCLUDED.ws_ins_max, " +
            "  ocdt_ws_ins_max = EXCLUDED.ocdt_ws_ins_max, wd_ins_max = EXCLUDED.wd_ins_max, " +
            "  ws_max = EXCLUDED.ws_max, ocdt_ws_max = EXCLUDED.ocdt_ws_max, " +
            "  wd_max = EXCLUDED.wd_max, wd_frq = EXCLUDED.wd_frq, " +
            "  ws_mix = EXCLUDED.ws_mix, wd_mix = EXCLUDED.wd_mix, geom = EXCLUDED.geom";

    private static final int CHUNK_SIZE = 1000;

    @Value("${dss.loader.wind.src:data/pss/wind/src}")
    private String windSrcPath;

    @Value("${dss.loader.wind.done:data/pss/wind/done}")
    private String windDonePath;

    public WindStatsLoader() {
        super(log);
    }

    @Override
    protected String getLoaderName() {
        return "바람 통계 데이터 (sm_wind_stats)";
    }

    @Override
    protected Path getSourceDirectory() {
        return Paths.get(windSrcPath);
    }

    @Override
    protected Path getProcessedDirectory() {
        return Paths.get(windDonePath);
    }

    @Override
    protected boolean isTargetFile(Path path) {
        return path.toString().endsWith(".txt") && path.getFileName().toString().contains("sts_wind");
    }

    @Override
    protected String getInsertSql() {
        return INSERT_SQL;
    }

    @Override
    protected int getChunkSize() {
        return CHUNK_SIZE;
    }

    @Override
    protected void moveProcessedFileGroup(Path sourceFile, Path processedDir) throws IOException {
        super.moveSingleFile(sourceFile, processedDir);
    }

    /**
     * 바람통계 텍스트 파일 파싱 (인코딩 안전 처리 포함)
     */
    @Override
    protected List<Object[]> parseFile(Path path) throws Exception {
        List<Object[]> batchArgs = new ArrayList<>();
        String filename = path.getFileName().toString();

        // 1. 인코딩 안전하게 Stream 가져오기 (FileDataLoader 스타일)
        try (Stream<String> lines = getSafeLinesStream(path)) {
            lines.forEach(line -> {
                line = line.trim();
                // 주석(#)이거나 빈 줄이면 스킵
                if (line.isEmpty() || line.startsWith("#")) {
                    return;
                }

                // 데이터 끝의 '=' 제거 후 공백 기준으로 분리
                String cleanLine = line.replace("=", "").trim();
                String[] tokens = cleanLine.split("\\s+");

                // ... (이하 데이터 매핑 로직은 동일)
                if (tokens.length >= 15) {
                    try {
                        Object[] row = new Object[16];
                        row[0]  = tokens[0];  // tm
                        row[1]  = tokens[1];  // stn_id
                        row[2]  = Double.parseDouble(tokens[2]); // lat
                        row[3]  = Double.parseDouble(tokens[3]); // lon
                        row[4]  = Double.parseDouble(tokens[4]); // altd
                        row[5]  = Double.parseDouble(tokens[5]); // ws_mavg
                        row[6]  = Double.parseDouble(tokens[6]); // ws_ins_max
                        row[7]  = tokens[7];  // ocdt_ws_ins_max
                        row[8]  = Double.parseDouble(tokens[8]); // wd_ins_max
                        row[9]  = Double.parseDouble(tokens[9]); // ws_max
                        row[10] = tokens[10]; // ocdt_ws_max
                        row[11] = Double.parseDouble(tokens[11]); // wd_max
                        row[12] = Double.parseDouble(tokens[12]); // wd_frq
                        row[13] = Double.parseDouble(tokens[13]); // ws_mix
                        row[14] = Double.parseDouble(tokens[14]); // wd_mix
                        row[15] = String.format("POINT(%s %s)", tokens[3], tokens[2]);

                        batchArgs.add(row);
                    } catch (NumberFormatException e) {
                        // 데이터 형식 오류 (숫자 변환 실패 등) - 해당 라인만 스킵하고 로그 기록
                        log.warn("[{}] 데이터 포맷 오류 (Line {}): {} | Raw Data: {}", filename, line, e.getMessage(), Arrays.toString(tokens));
                    } catch (Exception e) {
                        // 그 외 알 수 없는 시스템 오류 - 에러 로그 레벨 격상
                        log.error("[{}] 치명적인 파싱 오류 (Line {}): {}", filename, line, e.getMessage(), e);
                    }
                }
            });
        }
        return batchArgs;
    }

    /**
     * 파일 인코딩(UTF-8 -> MS949)을 순차적으로 시도하여 Stream 반환
     */
    private Stream<String> getSafeLinesStream(Path path) throws IOException {
        try {
            // 1. UTF-8 시도 (가장 권장됨)
            return Files.lines(path, StandardCharsets.UTF_8).collect(java.util.stream.Collectors.toList()).stream();
        } catch (java.io.UncheckedIOException | java.nio.charset.MalformedInputException e) {
            log.warn("[{}] UTF-8 디코딩 실패. MS949(EUC-KR)로 재시도합니다.", path.getFileName());
            // 2. MS949 시도 (기상청 로컬 파일 호환용)
            return Files.lines(path, java.nio.charset.Charset.forName("MS949")).collect(java.util.stream.Collectors.toList()).stream();
        }
    }
}