package com.emonster.taroaichat.service.llm.openrouter;

import com.emonster.taroaichat.config.ApplicationProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

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

/**
 * Direct HTTP client for OpenRouter API.
 * Replaces the OpenAI SDK for better control and compatibility.
 */
@Service
public class OpenRouterClient {

    private static final Logger LOG = LoggerFactory.getLogger(OpenRouterClient.class);

    private final RestTemplate restTemplate;
    private final WebClient.Builder webClientBuilder;
    private final ApplicationProperties applicationProperties;
    private final ObjectMapper objectMapper;

    public OpenRouterClient(RestTemplate restTemplate, WebClient.Builder webClientBuilder, ApplicationProperties applicationProperties, ObjectMapper objectMapper) {
        this.restTemplate = restTemplate;
        this.webClientBuilder = webClientBuilder;
        this.applicationProperties = applicationProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * Call OpenRouter chat completions API.
     */
    public ChatCompletionResponse createChatCompletion(ChatCompletionRequest request) {
        ApplicationProperties.OpenRouter config = applicationProperties.getOpenrouter();

        // Validate API key
        if (config.getApiKey() == null || config.getApiKey().isEmpty()) {
            LOG.error("OpenRouter API key is not configured. Please set openrouter.api-key in application properties.");
            throw new IllegalStateException("OpenRouter API key not configured.");
        }

        // Ensure base URL ends without trailing slash, then add endpoint
        String baseUrl = config.getBaseUrl();
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        String url = baseUrl + "/chat/completions";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(config.getApiKey());
        headers.set("HTTP-Referer", "https://tarot-ai-chat.com");
        headers.set("X-Title", "Tarot AI Chat");

        HttpEntity<ChatCompletionRequest> entity = new HttpEntity<>(request, headers);

        LOG.debug("Calling OpenRouter API at: {}", url);
        LOG.debug("Request model: {}", request.model);

        try {
            // Log the request body for debugging
            String requestJson = objectMapper.writeValueAsString(request);
            LOG.debug("Request body: {}", requestJson);
            // First, try to get the response as String to see what we're getting
            ResponseEntity<String> stringResponse = restTemplate.exchange(
                url,
                HttpMethod.POST,
                entity,
                String.class
            );

            LOG.debug("OpenRouter response status: {}", stringResponse.getStatusCode());
            LOG.debug("OpenRouter response body: {}", stringResponse.getBody());

            // If successful, parse the JSON
            if (stringResponse.getStatusCode().is2xxSuccessful()) {
                return objectMapper.readValue(stringResponse.getBody(), ChatCompletionResponse.class);
            } else {
                LOG.error("OpenRouter returned error status: {}, body: {}",
                    stringResponse.getStatusCode(), stringResponse.getBody());
                throw new RuntimeException("OpenRouter API returned error: " + stringResponse.getStatusCode());
            }

        } catch (HttpClientErrorException | HttpServerErrorException e) {
            LOG.error("OpenRouter API returned HTTP error: Status={}, Response Body={}",
                e.getStatusCode(), e.getResponseBodyAsString());
            throw new RuntimeException("OpenRouter API error: " + e.getStatusCode() + " - " + e.getResponseBodyAsString(), e);
        } catch (Exception e) {
            LOG.error("OpenRouter API call failed", e);
            throw new RuntimeException("Failed to call OpenRouter API", e);
        }
    }

    /**
     * Call OpenRouter chat completions API with streaming.
     */
    public Flux<String> streamChatCompletion(ChatCompletionRequest request) {
        ApplicationProperties.OpenRouter config = applicationProperties.getOpenrouter();
        if (config.getApiKey() == null || config.getApiKey().isEmpty()) {
            return Flux.error(new IllegalStateException("OpenRouter API key not configured."));
        }

        request.stream = true; // Ensure streaming is enabled

        String baseUrl = config.getBaseUrl();
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        String url = baseUrl + "/chat/completions";

        WebClient client = webClientBuilder.baseUrl(url).build();

        return client.post()
            .contentType(MediaType.APPLICATION_JSON)
            .headers(h -> {
                h.setBearerAuth(config.getApiKey());
                h.set("HTTP-Referer", "https://tarot-ai-chat.com");
                h.set("X-Title", "Tarot AI Chat");
            })
            .bodyValue(request)
            .retrieve()
            .bodyToFlux(String.class)
            .doOnError(error -> LOG.error("Error during OpenRouter stream", error))
            .doOnComplete(() -> LOG.info("OpenRouter stream completed"));
    }

    /**
     * Call OpenRouter chat completions API using reactive WebClient (non-blocking).
     * This is used for Phase 2 AI analysis to avoid blocking reactor threads.
     */
    public reactor.core.publisher.Mono<ChatCompletionResponse> createChatCompletionAsync(ChatCompletionRequest request) {
        ApplicationProperties.OpenRouter config = applicationProperties.getOpenrouter();

        // Validate API key
        if (config.getApiKey() == null || config.getApiKey().isEmpty()) {
            LOG.error("OpenRouter API key is not configured. Please set openrouter.api-key in application properties.");
            return reactor.core.publisher.Mono.error(new IllegalStateException("OpenRouter API key not configured."));
        }

        // Ensure streaming is disabled for regular completion
        request.stream = false;

        // Ensure base URL ends without trailing slash, then add endpoint
        String baseUrl = config.getBaseUrl();
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        String url = baseUrl + "/chat/completions";

        WebClient client = webClientBuilder.baseUrl(url).build();

        LOG.debug("Calling OpenRouter API (async) at: {}", url);
        LOG.debug("Request model: {}", request.model);

        try {
            // Log the request body for debugging
            String requestJson = objectMapper.writeValueAsString(request);
            LOG.debug("Request body: {}", requestJson);
        } catch (Exception e) {
            LOG.warn("Could not serialize request for logging", e);
        }

        return client.post()
            .contentType(MediaType.APPLICATION_JSON)
            .headers(h -> {
                h.setBearerAuth(config.getApiKey());
                h.set("HTTP-Referer", "https://tarot-ai-chat.com");
                h.set("X-Title", "Tarot AI Chat");
            })
            .bodyValue(request)
            .retrieve()
            .bodyToMono(ChatCompletionResponse.class)
            .doOnSuccess(response -> LOG.debug("OpenRouter async response received"))
            .doOnError(error -> LOG.error("Error during OpenRouter async call", error));
    }

    // Request/Response DTOs
    public static class ChatCompletionRequest {
        public String model;
        public List<Message> messages;
        public Double temperature;
        @JsonProperty("max_tokens")
        public Integer maxTokens;
        @JsonProperty("top_p")
        public Double topP;
        // Function calling support
        public List<Map<String, Object>> tools;
        @JsonProperty("tool_choice")
        public String toolChoice; // "auto", "none", or specific tool
        public Boolean stream; // Add stream parameter

        public static class Message {
            public String role;
            public String content;

            public Message(String role, String content) {
                this.role = role;
                this.content = content;
            }
        }
    }

    public static class ChatCompletionResponse {
        public String id;
        public String object;
        public Long created;
        public String model;
        public List<Choice> choices;
        public Usage usage;

        public static class Choice {
            public Integer index;
            public Message message;
            @JsonProperty("finish_reason")
            public String finishReason;

            public static class Message {
                public String role;
                public String content;
                @JsonProperty("tool_calls")
                public List<ToolCall> toolCalls;

                public static class ToolCall {
                    public String id;
                    public String type;
                    public Function function;

                    public static class Function {
                        public String name;
                        public String arguments; // JSON string of arguments
                    }
                }
            }
        }

        public static class Usage {
            @JsonProperty("prompt_tokens")
            public Integer promptTokens;
            @JsonProperty("completion_tokens")
            public Integer completionTokens;
            @JsonProperty("total_tokens")
            public Integer totalTokens;
        }
    }

    // DTO for streaming response chunks
    public static class ChatCompletionChunk {
        public String id;
        public String object;
        public Long created;
        public String model;
        public List<ChunkChoice> choices;

        public static class ChunkChoice {
            public Integer index;
            public Delta delta;
            @JsonProperty("finish_reason")
            public String finishReason;

            public static class Delta {
                public String role;
                public String content;
                @JsonProperty("tool_calls")
                public List<ToolCall> toolCalls;

                public static class ToolCall {
                    public Integer index;
                    public String id;
                    public String type;
                    public Function function;

                    public static class Function {
                        public String name;
                        public String arguments;
                    }
                }
            }
        }
    }
}
