package com.zioinfo.mes.rag;

import com.zioinfo.mes.admin.dto.MesSetting;
import com.zioinfo.mes.admin.mapper.SettingMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * MES RAG 기법 토글 서비스.
 *
 * <p>기존 {@code mes_setting}(key-value) 테이블을 재사용해 RAG 토글을 격리 저장한다(신규 설정 테이블
 * 신설 없음, admin 설정 화면과 자연 통합). 키 네임스페이스 {@code rag.*} 로 다른 설정과 충돌 회피.
 *
 * <p><b>서버 RAM 제약</b>: 무거운 기법(graphrag·rerank·tool_use·stream)은 <b>기본 off</b>.
 * retrieval_mode 기본 vector. step 상한·top_k 상한을 강제(폭주 차단).
 */
@Service
@RequiredArgsConstructor
public class RagToggleService {

    private final SettingMapper settingMapper;

    public static final String K_ENABLED = "rag.enabled";
    public static final String K_MODE = "rag.retrieval_mode";       // vector|hybrid|graph
    public static final String K_RERANK = "rag.rerank";             // true|false (무거움→기본 off)
    public static final String K_GRAPHRAG = "rag.graphrag";         // true|false (무거움→기본 off)
    public static final String K_HYBRID = "rag.hybrid";             // true|false (mode 보조 스위치)
    public static final String K_TOOL_USE = "rag.tool_use";         // true|false (에이전틱 /agent, 무거움→off)
    public static final String K_STRUCTURED = "rag.structured";     // true|false (구조화 출력, 기본 on)
    public static final String K_STREAM = "rag.stream";             // true|false (SSE, 무거움→기본 off)
    public static final String K_TOP_K = "rag.top_k";
    public static final String K_MAX_STEPS = "rag.max_steps";       // 에이전트 step 상한
    public static final String K_TEMPERATURE = "rag.temperature";
    public static final String K_GEN_MODEL = "rag.generation_model";

    /** 현재 토글 전부 조회(기본값 머지). 화면/배선 공용. */
    public RagToggles current() {
        Map<String, String> m = new LinkedHashMap<>();
        List<MesSetting> all = settingMapper.findAll();
        if (all != null) for (MesSetting s : all) m.put(s.getKey(), s.getValue());

        RagToggles t = new RagToggles();
        t.ragEnabled = bool(m.get(K_ENABLED), true);
        t.retrievalMode = mode(m.get(K_MODE));
        t.rerank = bool(m.get(K_RERANK), false);          // 무거움→off 기본
        t.graphrag = bool(m.get(K_GRAPHRAG), false);      // 무거움→off 기본
        t.hybrid = bool(m.get(K_HYBRID), false);          // 무거움→off 기본
        t.toolUse = bool(m.get(K_TOOL_USE), false);       // 무거움→off 기본(에이전틱)
        t.structured = bool(m.get(K_STRUCTURED), true);   // 결정론 출력→on 기본(경량)
        t.stream = bool(m.get(K_STREAM), false);          // 무거움→off 기본
        t.topK = clampInt(m.get(K_TOP_K), 5, 1, 20);
        t.maxSteps = clampInt(m.get(K_MAX_STEPS), 4, 1, 8);
        t.temperature = clampDouble(m.get(K_TEMPERATURE), 0.1, 0.0, 1.0);
        t.generationModel = blankTo(m.get(K_GEN_MODEL), "llama3.2:1b");
        return t;
    }

    /**
     * retrieval_mode 결정 우선순위: graphrag 토글 on → graph, 아니면 hybrid 토글 on → hybrid,
     * 아니면 설정된 mode(기본 vector).
     */
    public String effectiveMode(RagToggles t) {
        if (t.graphrag) return "graph";
        if (t.hybrid) return "hybrid";
        return t.retrievalMode;
    }

    // ── 정규화/클램프 (상한 강제 — 폭주·RAM 위협 차단) ────────────────────────
    private boolean bool(String v, boolean def) {
        if (v == null) return def;
        String s = v.trim();
        return "true".equalsIgnoreCase(s) || "1".equals(s) || "on".equalsIgnoreCase(s);
    }

    private String mode(String v) {
        if (v == null) return "vector";
        String s = v.trim().toLowerCase();
        return (s.equals("hybrid") || s.equals("graph") || s.equals("vector")) ? s : "vector";
    }

    private int clampInt(String v, int def, int min, int max) {
        try {
            int n = Integer.parseInt(v.trim());
            return Math.max(min, Math.min(max, n));
        } catch (Exception e) {
            return def;
        }
    }

    private double clampDouble(String v, double def, double min, double max) {
        try {
            double n = Double.parseDouble(v.trim());
            return Math.max(min, Math.min(max, n));
        } catch (Exception e) {
            return def;
        }
    }

    private String blankTo(String v, String def) {
        return (v == null || v.isBlank()) ? def : v.trim();
    }

    /** 토글 스냅샷 DTO(응답 metadata·화면 공용). */
    public static class RagToggles {
        public boolean ragEnabled = true;
        public String retrievalMode = "vector";
        public boolean rerank = false;
        public boolean graphrag = false;
        public boolean hybrid = false;
        public boolean toolUse = false;
        public boolean structured = true;
        public boolean stream = false;
        public int topK = 5;
        public int maxSteps = 4;
        public double temperature = 0.1;
        public String generationModel = "llama3.2:1b";

        public Map<String, Object> toMap() {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("ragEnabled", ragEnabled);
            m.put("retrievalMode", retrievalMode);
            m.put("rerank", rerank);
            m.put("graphrag", graphrag);
            m.put("hybrid", hybrid);
            m.put("toolUse", toolUse);
            m.put("structured", structured);
            m.put("stream", stream);
            m.put("topK", topK);
            m.put("maxSteps", maxSteps);
            m.put("temperature", temperature);
            m.put("generationModel", generationModel);
            return m;
        }
    }
}
