package com.zioinfo.kintex.ai;

import com.zioinfo.kintex.ai.TextAiClient.GenResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * F100 AI provider 선택·폴백 라우터. 모든 AI 텍스트 생성의 단일 진입점.
 *
 * <p><b>선택/폴백 정책</b>:
 * <ul>
 *   <li>AI 비활성({@link AiConfigService#aiEnabled()}=false) → 즉시 degraded(provider="none"). 호출자 폴백.</li>
 *   <li>provider=claude · 키 설정 · 활성 → Claude 시도. 실패(degraded)면 <b>Ollama 자동 폴백</b>.</li>
 *   <li>provider=ollama (또는 claude 인데 키 미설정) → Ollama 시도.</li>
 *   <li>양 경로 실패 → degraded(text=null). 호출자가 규칙기반/오류 처리(무회귀).</li>
 * </ul>
 *
 * <p><b>환각 방지</b>: 반환 {@link AiResult} 는 실제 응답 provider 와 degraded 를 정직하게 담는다 —
 * 호출자는 이를 근거로 AI 산출물 라벨링(AiLabel)·폴백을 결정한다.
 */
@Service
public class AiTextRouter {

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

    private final AiConfigService config;
    private final ClaudeTextClient claudeClient;
    private final OllamaTextClient ollamaClient;

    public AiTextRouter(AiConfigService config, ClaudeTextClient claudeClient, OllamaTextClient ollamaClient) {
        this.config = config;
        this.claudeClient = claudeClient;
        this.ollamaClient = ollamaClient;
    }

    /**
     * AI 텍스트 생성 결과.
     *
     * @param text     생성 텍스트(degraded 시 null)
     * @param provider 실제 응답 provider("claude"|"ollama"|"none")
     * @param degraded 폴백/실패 여부(true 면 호출자가 규칙기반/오류로 처리)
     */
    public record AiResult(String text, String provider, boolean degraded) {

        public boolean usable() {
            return !degraded && text != null && !text.isBlank();
        }

        static AiResult none() {
            return new AiResult(null, "none", true);
        }
    }

    /** 기본 max_tokens 로 생성. */
    public AiResult generate(String prompt) {
        return generate(prompt, 0);
    }

    /** max_tokens 상한을 지정해 생성(장문 1회 호출). Claude 경로에만 상한 반영, Ollama 는 기본 옵션. */
    public AiResult generate(String prompt, int maxTokens) {
        if (prompt == null || prompt.isBlank() || !config.aiEnabled()) {
            return AiResult.none();
        }
        if (config.isClaudeActive()) {
            GenResult r = maxTokens > 0
                    ? claudeClient.generate(prompt, config.claudeModel(), maxTokens)
                    : claudeClient.generate(prompt, config.claudeModel());
            if (r.usable()) {
                return new AiResult(r.text(), AiConfigService.PROVIDER_CLAUDE, false);
            }
            log.warn("Claude path degraded -> Ollama fallback");
            GenResult f = ollamaClient.generate(prompt, config.ollamaModel());
            return f.usable()
                    ? new AiResult(f.text(), AiConfigService.PROVIDER_OLLAMA, false)
                    : AiResult.none();
        }
        // provider=ollama, 또는 claude 인데 키 미설정 → Ollama 경로.
        GenResult o = ollamaClient.generate(prompt, config.ollamaModel());
        return o.usable()
                ? new AiResult(o.text(), AiConfigService.PROVIDER_OLLAMA, false)
                : AiResult.none();
    }
}
