package com.zioinfo.hrm.wise;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * WISE AI — 얇은 중앙 guardia-rag REST 클라이언트 (HRM 전용). [GUARDiA-HRM]
 *
 * <p><b>핵심 설계</b>: HRM 은 LangChain·벡터 로직을 Java 에 재구현하지 않는다. 중앙 Python
 * guardia-rag 의 검색·근거생성·인용·guardrail 을 {@code POST /rag/answer} 로 호출하기만 한다.
 * 미가용/타임아웃/RAM 부족 시 예외를 전파하지 않고 {@code degraded:true} 폴백을 돌려준다.
 *
 * <p><b>보안 불변</b>: base-url 은 온프레미스(중앙 guardia-rag) 전용. 외부 LLM 직접 호출 없음.
 * 응답에서 자격증명/PII/스택트레이스 미노출. 오류는 1줄 요약만 로깅.
 */
@Slf4j
@Component
public class RagClient {

    /** 솔루션 격리 키(컬렉션 rag_hrm·피드백·설정). */
    private static final String SOLUTION = "hrm";

    /** /rag/answer 콜드로드 감안 타임아웃(WISE_APPLY_SPEC: 240s). */
    private static final Duration ANSWER_TIMEOUT = Duration.ofSeconds(240);

    /** 중앙 guardia-rag 베이스 URL(서버 내부 루프백 기본). 외부 URL 설정 금지. */
    @Value("${guardia.rag.base-url:http://127.0.0.1:8020}")
    private String baseUrl;

    /** RAG 경유 마스터 스위치. false 면 항상 degraded(HRM 화면은 안내 메시지). */
    @Value("${guardia.rag.enabled:true}")
    private boolean enabled;

    private final HttpClient http = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(5))
            .build();
    private final ObjectMapper mapper = new ObjectMapper();

    /**
     * POST /rag/answer — 검색+근거생성+인용+guardrail(근거 부족 시 abstained 보류).
     *
     * @return {@code {answer, sources[], grounded, faithfulness, abstained, degraded, degraded_reason, trace_id, answer_id}}.
     *         미가용 시 {@code {degraded:true, degraded_reason:...}}.
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> answer(String query) {
        if (!enabled) {
            return degraded("rag_disabled");
        }
        try {
            Map<String, Object> options = new LinkedHashMap<>();
            options.put("stream", false);
            options.put("verify", true);

            Map<String, Object> body = new LinkedHashMap<>();
            body.put("solution", SOLUTION);
            body.put("query", query == null ? "" : query);
            body.put("retrieval_mode", "vector");
            body.put("top_k", 5);
            body.put("options", options);

            String json = mapper.writeValueAsString(body);
            HttpRequest req = HttpRequest.newBuilder()
                    .uri(URI.create(trimTrailingSlash(baseUrl) + "/rag/answer"))
                    .timeout(ANSWER_TIMEOUT)
                    .header("Content-Type", "application/json")
                    .header("X-Solution-Key", SOLUTION)
                    .POST(HttpRequest.BodyPublishers.ofString(json))
                    .build();

            HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
            if (resp.statusCode() / 100 != 2 || resp.body() == null || resp.body().isBlank()) {
                log.warn("중앙 rag /answer 비2xx 또는 빈 응답({}) — HRM 폴백", resp.statusCode());
                return degraded("rag_unavailable");
            }
            Map<String, Object> out = mapper.readValue(resp.body(), Map.class);
            return out != null ? out : degraded("rag_no_response");
        } catch (Exception e) {
            log.warn("중앙 rag /answer 일시 불가 — HRM 폴백: {}", summarize(e));
            return degraded("rag_unavailable");
        }
    }

    private static Map<String, Object> degraded(String reason) {
        Map<String, Object> out = new LinkedHashMap<>();
        out.put("degraded", true);
        out.put("degraded_reason", reason);
        return out;
    }

    private static String trimTrailingSlash(String s) {
        if (s == null || s.isBlank()) {
            return "http://127.0.0.1:8020";
        }
        return s.endsWith("/") ? s.substring(0, s.length() - 1) : s;
    }

    /** 스택트레이스 미노출: 메시지 1줄만 요약. */
    private static String summarize(Exception e) {
        String m = e.getMessage();
        if (m == null) {
            return e.getClass().getSimpleName();
        }
        return m.length() > 160 ? m.substring(0, 160) : m;
    }
}
