package com.zioinfo.esn.ai.service;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.regex.Pattern;

/**
 * 로컬 임베디드 DuckDB AI 학습·분석 저장소. [ZIOINFO-ESN]
 *
 * <p><b>목적</b>: AI 추론 로그(ai_infer_log)와 사용자 피드백(ai_feedback)을 솔루션 로컬 DuckDB 파일에
 * 축적해 오프라인 AI 분석/평가에 사용한다(중앙 guardia-rag 는 통합 학습 게이트, 로컬 DuckDB 는 솔루션별).
 *
 * <p><b>경로</b>: {@code /opt/zioinfo-esn/data/esn_learning.duckdb}(prop {@code guardia.duckdb-path}).
 * 스키마는 멱등(IF NOT EXISTS). 파일 격리 — 솔루션별 단일 파일.
 *
 * <p><b>Fail-Safe</b>: DuckDB 드라이버/파일/디렉터리 미가용 시 조용히 비활성(no-op)한다 —
 * 학습 저장 실패가 AI 응답/서비스 기동을 절대 막지 않는다. 단일 커넥션 동기화.
 *
 * <p><b>PII 마스킹</b>: question/answer/correction/user 저장 전 주민번호·카드·전화·이메일을 마스킹한다.
 * 자격증명/키/스택트레이스는 애초에 수집하지 않는다.
 */
@Slf4j
@Service
public class LearningStore {

    public static final String SOLUTION = "zioinfo-esn";

    private static final Pattern RRN = Pattern.compile("\\d{6}[- ]?\\d{7}");
    private static final Pattern CARD = Pattern.compile("\\b(?:\\d[ -]?){13,16}\\b");
    private static final Pattern PHONE = Pattern.compile("01[016-9][- ]?\\d{3,4}[- ]?\\d{4}");
    private static final Pattern EMAIL = Pattern.compile("[\\w.+-]+@[\\w.-]+\\.[A-Za-z]{2,}");

    @Value("${guardia.duckdb-path:/opt/zioinfo-esn/data/esn_learning.duckdb}")
    private String duckdbPath;

    /** null 이면 비활성(no-op). */
    private volatile Connection conn;

    @PostConstruct
    void init() {
        try {
            Class.forName("org.duckdb.DuckDBDriver");
            File f = new File(duckdbPath);
            File dir = f.getParentFile();
            if (dir != null && !dir.exists() && !dir.mkdirs()) {
                log.warn("DuckDB 디렉터리 생성 불가 — 학습 저장 비활성: {}", dir.getPath());
                return;
            }
            this.conn = DriverManager.getConnection("jdbc:duckdb:" + duckdbPath);
            try (Statement st = conn.createStatement()) {
                st.execute("CREATE TABLE IF NOT EXISTS ai_feedback ("
                        + "id VARCHAR PRIMARY KEY, ts TIMESTAMP, solution VARCHAR, feature VARCHAR, "
                        + "question VARCHAR, answer VARCHAR, verdict VARCHAR, correction VARCHAR, user_masked VARCHAR)");
                st.execute("CREATE TABLE IF NOT EXISTS ai_infer_log ("
                        + "id VARCHAR PRIMARY KEY, ts TIMESTAMP, provider VARCHAR, model VARCHAR, "
                        + "latency_ms BIGINT, degraded BOOLEAN)");
            }
            log.info("DuckDB 학습 저장소 초기화 완료: {}", duckdbPath);
        } catch (Throwable t) {
            // 드라이버 미탑재/파일락/권한 등 — 조용히 비활성(무회귀). 값/경로만, 시크릿 없음.
            this.conn = null;
            log.warn("DuckDB 학습 저장소 비활성(no-op): {}", t.getClass().getSimpleName());
        }
    }

    /** 활성 여부(테스트/진단용). */
    public boolean isEnabled() {
        return conn != null;
    }

    /** 추론 로그 1건 기록(실패 시 무시). AiTextRouter 가 provider/model/latency/degraded 로 호출. */
    public synchronized void logInference(String provider, String model, long latencyMs, boolean degraded) {
        if (conn == null) {
            return;
        }
        try (PreparedStatement ps = conn.prepareStatement(
                "INSERT INTO ai_infer_log(id, ts, provider, model, latency_ms, degraded) VALUES (?,?,?,?,?,?)")) {
            ps.setString(1, UUID.randomUUID().toString());
            ps.setObject(2, LocalDateTime.now());
            ps.setString(3, provider);
            ps.setString(4, model);
            ps.setLong(5, latencyMs);
            ps.setBoolean(6, degraded);
            ps.executeUpdate();
        } catch (Exception e) {
            log.debug("ai_infer_log 기록 실패(무시): {}", e.getClass().getSimpleName());
        }
    }

    /** 피드백 1건 기록(PII 마스킹). verdict 예: UP/DOWN. 실패 시 무시. */
    public synchronized void saveFeedback(String feature, String question, String answer,
                                          String verdict, String correction, String user) {
        if (conn == null) {
            return;
        }
        try (PreparedStatement ps = conn.prepareStatement(
                "INSERT INTO ai_feedback(id, ts, solution, feature, question, answer, verdict, correction, user_masked) "
                        + "VALUES (?,?,?,?,?,?,?,?,?)")) {
            ps.setString(1, UUID.randomUUID().toString());
            ps.setObject(2, LocalDateTime.now());
            ps.setString(3, SOLUTION);
            ps.setString(4, feature);
            ps.setString(5, mask(question));
            ps.setString(6, mask(answer));
            ps.setString(7, verdict);
            ps.setString(8, mask(correction));
            ps.setString(9, mask(user));
            ps.executeUpdate();
        } catch (Exception e) {
            log.debug("ai_feedback 기록 실패(무시): {}", e.getClass().getSimpleName());
        }
    }

    /** PII 마스킹(주민번호·카드·전화·이메일). null 안전. 중앙 전달분 마스킹에도 재사용. */
    public static String mask(String s) {
        if (s == null || s.isBlank()) {
            return s;
        }
        String out = s;
        out = RRN.matcher(out).replaceAll("######-#######");
        out = CARD.matcher(out).replaceAll("****-****-****-****");
        out = PHONE.matcher(out).replaceAll("***-****-****");
        out = EMAIL.matcher(out).replaceAll("***@***");
        return out;
    }
}
