package com.erp.ai.client;

import com.erp.ai.exception.LlmClientException;
import com.erp.ai.mapper.AiLlmProviderMapper;
import com.erp.ai.model.AiLlmProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientResponseException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Ollama 로컬 LLM 클라이언트.
 * /api/chat 엔드포인트.
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OllamaLlmClient implements LlmClient {

    private static final String DEFAULT_BASE_URL = "http://localhost:11434";

    @Qualifier("llmRestClient")
    private final RestClient httpClient;
    private final AiLlmProviderMapper providerMapper;

    @Override
    public boolean supports(String model) {
        // Ollama는 사용자 정의 모델명. 하위 호환을 위해 다른 클라이언트가 지원 안하는 경우 fallback.
        // 명시적 prefix: "ollama:" 또는 "llama-" / "mistral-" 등
        if (model == null) return false;
        String lower = model.toLowerCase();
        return lower.startsWith("ollama:")
                || lower.startsWith("llama")
                || lower.startsWith("mistral")
                || lower.startsWith("qwen")
                || lower.startsWith("phi");
    }

    @Override
    public String providerName() {
        return "ollama";
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> chat(Map<String, Object> request) {
        AiLlmProvider provider = providerMapper.getByName("ollama");
        String baseUrl = provider != null && provider.getEndpoint() != null ? provider.getEndpoint() : DEFAULT_BASE_URL;

        String model = String.valueOf(request.get("model"));
        if (model.startsWith("ollama:")) model = model.substring(7);

        Map<String, Object> body = new HashMap<>();
        body.put("model", model);
        body.put("messages", request.getOrDefault("messages", List.of()));
        body.put("stream", false);
        Map<String, Object> options = new HashMap<>();
        if (request.get("max_tokens") != null) options.put("num_predict", request.get("max_tokens"));
        if (request.get("temperature") != null) options.put("temperature", request.get("temperature"));
        if (!options.isEmpty()) body.put("options", options);

        try {
            Map<String, Object> resp = httpClient.post()
                    .uri(baseUrl + "/api/chat")
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(body)
                    .retrieve()
                    .body(Map.class);
            return convertToOpenAiFormat(resp, model);
        } catch (RestClientResponseException e) {
            log.error("Ollama API 오류 {}: {}", e.getStatusCode().value(), e.getResponseBodyAsString());
            throw new LlmClientException("Ollama API 오류: " + e.getMessage(), e.getStatusCode().value());
        } catch (Exception e) {
            throw new LlmClientException("Ollama 호출 실패: " + e.getMessage(), e);
        }
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> convertToOpenAiFormat(Map<String, Object> ollama, String model) {
        if (ollama == null) return Map.of();
        Map<String, Object> message = (Map<String, Object>) ollama.getOrDefault("message", Map.of());
        String content = String.valueOf(message.getOrDefault("content", ""));

        Number promptCount = (Number) ollama.getOrDefault("prompt_eval_count", 0);
        Number evalCount = (Number) ollama.getOrDefault("eval_count", 0);

        Map<String, Object> usageOut = new HashMap<>();
        usageOut.put("prompt_tokens", promptCount.intValue());
        usageOut.put("completion_tokens", evalCount.intValue());
        usageOut.put("total_tokens", promptCount.intValue() + evalCount.intValue());

        List<Map<String, Object>> choices = new ArrayList<>();
        choices.add(Map.of(
                "index", 0,
                "message", Map.of("role", "assistant", "content", content),
                "finish_reason", "stop"
        ));

        Map<String, Object> out = new HashMap<>();
        out.put("id", "chatcmpl-" + UUID.randomUUID());
        out.put("object", "chat.completion");
        out.put("created", System.currentTimeMillis() / 1000);
        out.put("model", model);
        out.put("choices", choices);
        out.put("usage", usageOut);
        return out;
    }
}
