package com.zioinfo.cms.ai;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

import java.time.Duration;
import java.util.List;
import java.util.Map;

/**
 * Ollama 온프레미스 LLM 클라이언트.
 *
 * <p>보안 불변 규칙: localhost Ollama만 호출. 외부 AI API 절대 금지.
 * 오프라인/장애 시 절대 예외를 던지지 않고 빈 문자열을 반환한다(서비스 계층이 Java 폴백 수행).
 */
@Slf4j
@Component
public class OllamaClient {

    private final WebClient.Builder builder;
    private final String ollamaUrl;
    private final String textModel;
    private final String visionModel;

    public OllamaClient(WebClient.Builder builder,
                        @Value("${guardia.ollama-url:http://localhost:11434}") String ollamaUrl,
                        @Value("${guardia.ollama-text-model:llama3}") String textModel,
                        @Value("${guardia.ollama-vision-model:llava}") String visionModel) {
        this.builder = builder;
        this.ollamaUrl = ollamaUrl;
        this.textModel = textModel;
        this.visionModel = visionModel;
    }

    /** 프롬프트로 텍스트 생성(기본 텍스트 모델). 실패 시 빈 문자열 반환(예외 없음). */
    public String generate(String prompt) {
        return generate(prompt, textModel);
    }

    /**
     * 지정 모델로 텍스트 생성. 실패 시 빈 문자열 반환(예외 없음).
     *
     * <p>{@code AiTextRouter}·{@code AiConfigService} 가 provider(qwen3/deepseek/glm/ollama)별 소형 모델을
     * 지정 호출하기 위한 진입점. model 이 비었으면 서버 기본 텍스트 모델로 폴백한다.
     */
    @SuppressWarnings("unchecked")
    public String generate(String prompt, String model) {
        String useModel = (model == null || model.isBlank()) ? textModel : model.trim();
        try {
            Map<String, Object> body = Map.of("model", useModel, "prompt", prompt, "stream", false);
            Map<String, Object> res = builder.baseUrl(ollamaUrl).build()
                .post().uri("/api/generate")
                .bodyValue(body)
                .retrieve()
                .bodyToMono(Map.class)
                .timeout(Duration.ofSeconds(120))
                .map(m -> (Map<String, Object>) m)
                .block();
            if (res == null) return "";
            Object r = res.get("response");
            return r == null ? "" : String.valueOf(r).trim();
        } catch (Exception e) {
            log.warn("Ollama 일시 불가 — Java 폴백 사용: {}", e.getMessage());
            return "";
        }
    }

    /** llava 비전 모델로 이미지(base64) 분석. 실패 시 빈 문자열. */
    @SuppressWarnings("unchecked")
    public String vision(String prompt, String imageBase64) {
        try {
            Map<String, Object> body = Map.of(
                "model", visionModel,
                "prompt", prompt,
                "images", List.of(imageBase64),
                "stream", false);
            Map<String, Object> res = builder.baseUrl(ollamaUrl).build()
                .post().uri("/api/generate")
                .bodyValue(body)
                .retrieve()
                .bodyToMono(Map.class)
                .timeout(Duration.ofSeconds(120))
                .map(m -> (Map<String, Object>) m)
                .block();
            if (res == null) return "";
            Object r = res.get("response");
            return r == null ? "" : String.valueOf(r).trim();
        } catch (Exception e) {
            log.warn("Ollama 비전 일시 불가 — Java 폴백 사용: {}", e.getMessage());
            return "";
        }
    }

    public boolean available() {
        try {
            builder.baseUrl(ollamaUrl).build().get().uri("/api/tags")
                .retrieve().bodyToMono(String.class).timeout(Duration.ofSeconds(3)).block();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
