package de.assecutor.votianlt.ai.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
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;

/**
 * Direct REST client for LM Studio API. Uses Spring WebClient like
 * aimailassistant - bypasses Spring AI.
 */
@Component
@Slf4j
public class LlmRestClient {

    private final WebClient webClient;
    private final ObjectMapper objectMapper;
    private final String model;

    public LlmRestClient(@Value("${spring.ai.openai.base-url:http://192.168.180.3:1234}") String baseUrl,
            @Value("${spring.ai.openai.chat.options.model:local-model}") String model, ObjectMapper objectMapper) {

        this.webClient = WebClient.builder().baseUrl(baseUrl + "/v1/chat/completions").build();
        this.model = model;
        this.objectMapper = objectMapper;

        log.info("LlmRestClient initialized - URL: {}/v1/chat/completions, Model: {}", baseUrl, model);
    }

    /**
     * Send a chat completion request to LM Studio.
     *
     * @param systemPrompt
     *            System prompt for context
     * @param userMessage
     *            User message/question
     * @return LLM response text, or null on error
     */
    public String chat(String systemPrompt, String userMessage) {
        return chat(systemPrompt, userMessage, 0.7, 2000);
    }

    /**
     * Send a chat completion request to LM Studio with custom parameters.
     *
     * @param systemPrompt
     *            System prompt for context
     * @param userMessage
     *            User message/question
     * @param temperature
     *            Temperature for response randomness (0.0-1.0)
     * @param maxTokens
     *            Maximum tokens in response
     * @return LLM response text, or null on error
     */
    public String chat(String systemPrompt, String userMessage, double temperature, int maxTokens) {
        try {
            Map<String, Object> request = Map.of("model", model, "messages",
                    List.of(Map.of("role", "system", "content", systemPrompt != null ? systemPrompt : ""),
                            Map.of("role", "user", "content", userMessage)),
                    "temperature", temperature, "max_tokens", maxTokens, "stream", false // WICHTIG: Kein Streaming!
            );

            log.info("Sending request to LLM (model: {}, prompt length: {} chars)...", model, userMessage.length());
            long startTime = System.currentTimeMillis();

            String response = webClient.post().contentType(MediaType.APPLICATION_JSON).bodyValue(request).retrieve()
                    .bodyToMono(String.class).timeout(Duration.ofSeconds(120)).block();

            long duration = System.currentTimeMillis() - startTime;
            log.info("LLM response received in {}ms", duration);
            log.debug("Raw LLM response: {}", response);

            return extractContent(response);

        } catch (Exception e) {
            log.error("Error calling LLM API: {} - {}", e.getClass().getSimpleName(), e.getMessage());
            if (log.isDebugEnabled()) {
                log.debug("Full stack trace:", e);
            }
            return null;
        }
    }

    /**
     * Simple chat without system prompt.
     */
    public String chat(String userMessage) {
        return chat(null, userMessage);
    }

    private String extractContent(String response) {
        if (response == null || response.isBlank()) {
            log.warn("LLM returned null or blank response");
            return null;
        }
        try {
            JsonNode root = objectMapper.readTree(response);
            JsonNode choices = root.path("choices");
            if (choices.isArray() && !choices.isEmpty()) {
                String content = choices.get(0).path("message").path("content").asText();
                // asText() returns empty string for null/missing nodes - treat as null
                if (content == null || content.isBlank()) {
                    log.warn("LLM response content is empty");
                    return null;
                }
                return content;
            }
            log.warn("Unexpected response structure (no choices): {}", response);
            return null;
        } catch (Exception e) {
            log.error("Error parsing LLM response: {}", e.getMessage());
            return null;
        }
    }
}
