using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using LanguageDetection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using libObsidean;

namespace GTPCorrgir
{
    public class ApiSettings
    {
        public class ApiKeySection
        {
            public string OpenAI { get; set; }
            public string Groq { get; set; }
            public string Grok { get; set; }
            public string Claude { get; set; }
        }

        public ApiKeySection ApiKeys { get; set; }
    }

    public class gtpask : IDisposable
    {
        private string _openAiApiKey;
        private string _groqApiKey;
        private string _grokApiKey;
        private string _claudeApiKey;
        private readonly HttpClient _httpClient;
        private bool _disposed;
        private readonly LanguageDetector _languageDetector;
        private readonly Obsidean _markdownProcessor;

        public Logger Log { get; }

        private readonly Dictionary<string, string> _languageMap = new Dictionary<string, string>
        {
            { "en", "English" },
            { "es", "Spanish" },
            { "it", "Italian" },
            { "pt", "Portuguese" }
        };

        public string IdiomaDetectado { get; private set; }
        public string TextoACorregir { get; set; }
        public string TextoCorregido { get; private set; }
        public string TextodeSistema { get; private set; }
        private const bool Simulacion = false;

        public gtpask()
        {
            try
            {
                Log = Logger.Instance;
                _httpClient = new HttpClient();
                _languageDetector = new LanguageDetector();
                _languageDetector.AddLanguages("en", "es", "it", "pt");
                _markdownProcessor = new Obsidean();

                LoadApiKeys();
                InitializeHttpClient();
                _markdownProcessor.LeerPalabrasTecnicas();

                Log.Log("gtpask initialized successfully");
            }
            catch (Exception ex)
            {
                Log.Log($"Error initializing gtpask: {ex.Message}");
                throw new ApplicationException("Failed to initialize gtpask", ex);
            }
        }

        private void LoadApiKeys()
        {
            try
            {
                string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
                if (!File.Exists(configPath))
                {
                    throw new FileNotFoundException("Configuration file (appsettings.json) not found.");
                }

                string jsonContent = File.ReadAllText(configPath);
                var settings = JsonConvert.DeserializeObject<ApiSettings>(jsonContent);

                _openAiApiKey = settings?.ApiKeys?.OpenAI;
                _groqApiKey = settings?.ApiKeys?.Groq;
                _grokApiKey = settings?.ApiKeys?.Grok;
                _claudeApiKey = settings?.ApiKeys?.Claude;

                ValidateApiKeys();
            }
            catch (Exception ex)
            {
                Log.Log($"Error loading API keys: {ex.Message}");
                throw new ApplicationException("Failed to load API keys", ex);
            }
        }

        private void ValidateApiKeys()
        {
            var missingKeys = new List<string>();

            if (string.IsNullOrEmpty(_openAiApiKey)) missingKeys.Add("OpenAI");
            if (string.IsNullOrEmpty(_groqApiKey)) missingKeys.Add("Groq");
            if (string.IsNullOrEmpty(_grokApiKey)) missingKeys.Add("Grok");
            if (string.IsNullOrEmpty(_claudeApiKey)) missingKeys.Add("Claude");

            if (missingKeys.Any())
            {
                string missingKeysStr = string.Join(", ", missingKeys);
                throw new ApplicationException($"Missing API keys: {missingKeysStr}");
            }
        }

        private void InitializeHttpClient()
        {
            _httpClient.Timeout = TimeSpan.FromSeconds(60);
            _httpClient.DefaultRequestHeaders.Clear();
            _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
        }

        public async Task CorregirTexto()
        {
            if (string.IsNullOrWhiteSpace(TextoACorregir))
            {
                Log.Log("No hay texto para corregir");
                return;
            }

            try
            {
                Log.Log("Iniciando proceso de corrección");
                Log.Log($"Texto original: {TextoACorregir}");

                if (!DetectarIdioma())
                {
                    throw new ApplicationException("No se pudo detectar el idioma del texto");
                }

                string textoMarcado = MarcarPalabrasTecnicas();
                Log.Log($"Texto marcado: {textoMarcado}");

                if (Simulacion)
                {
                    await SimularCorreccion();
                }
                else
                {
                    await ProcesarTextoConLLM(textoMarcado);
                }

                Log.Log($"Texto corregido: {TextoCorregido}");
            }
            catch (Exception ex)
            {
                Log.Log($"Error en CorregirTexto: {ex.Message}");
                throw;
            }
        }

        private bool DetectarIdioma()
        {
            try
            {
                if (EsModoTraduccion())
                {
                    IdiomaDetectado = ObtenerIdiomaObjetivo();
                    Log.Log($"Modo traducción: idioma objetivo establecido a {IdiomaDetectado}");
                    return true;
                }

                // Si el texto es solo una URL, usar español como predeterminado
                if (EsSoloURL(TextoACorregir))
                {
                    IdiomaDetectado = _languageMap["es"]; // Español como predeterminado
                    Log.Log($"Texto es URL, usando idioma predeterminado: {IdiomaDetectado}");
                    return true;
                }

                // Si el texto es muy corto, usar español como predeterminado
                if (string.IsNullOrWhiteSpace(TextoACorregir) || TextoACorregir.Trim().Length < 10)
                {
                    IdiomaDetectado = _languageMap["es"]; // Español como predeterminado
                    Log.Log($"Texto muy corto, usando idioma predeterminado: {IdiomaDetectado}");
                    return true;
                }

                string detectedLanguageCode = _languageDetector.Detect(TextoACorregir);

                // Si no se detectó idioma o es desconocido, usar español como fallback
                if (string.IsNullOrEmpty(detectedLanguageCode) || !_languageMap.ContainsKey(detectedLanguageCode))
                {
                    IdiomaDetectado = _languageMap["es"]; // Español como fallback
                    Log.Log($"Idioma no detectado, usando fallback: {IdiomaDetectado}");
                    return true;
                }

                IdiomaDetectado = _languageMap[detectedLanguageCode];
                Log.Log($"Idioma detectado: {IdiomaDetectado}");
                return true;
            }
            catch (Exception ex)
            {
                Log.Log($"Error al detectar idioma: {ex.Message}");
                // En caso de error, usar español como fallback
                IdiomaDetectado = _languageMap["es"];
                Log.Log($"Error en detección, usando fallback: {IdiomaDetectado}");
                return true;
            }
        }

        private bool EsModoTraduccion()
        {
            return Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Ingles ||
                   Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano ||
                   Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Espanol ||
                   Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Portugues;
        }

        private string ObtenerIdiomaObjetivo()
        {
            return Opciones.Instance.modo switch
            {
                Opciones.modoDeUso.Traducir_a_Ingles => _languageMap["en"],
                Opciones.modoDeUso.Traducir_a_Italiano => _languageMap["it"],
                Opciones.modoDeUso.Traducir_a_Espanol => _languageMap["es"],
                Opciones.modoDeUso.Traducir_a_Portugues => _languageMap["pt"],
                _ => throw new ArgumentException("Modo de traducción no válido")
            };
        }

        private string MarcarPalabrasTecnicas()
        {
            try
            {
                return _markdownProcessor.MarkTechnicalTerms_IgnoreCase(TextoACorregir);
            }
            catch (Exception ex)
            {
                Log.Log($"Error al marcar palabras técnicas: {ex.Message}");
                throw;
            }
        }

        private async Task ProcesarTextoConLLM(string textoMarcado)
        {
            try
            {
                string respuestaLLM;
                string respuestaCompleta = "";

                // Si es el modo Claude Web Search, usar el método específico
                if (Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch)
                {
                    var resultadoWebSearch = await CallClaudeWebSearchApiCompleto(textoMarcado);
                    respuestaLLM = resultadoWebSearch.texto;
                    respuestaCompleta = resultadoWebSearch.respuestaCompleta;
                }
                else
                {
                    switch (Opciones.Instance.LLM)
                    {
                        case Opciones.LLM_a_Usar.OpenAI:
                            respuestaLLM = await CallOpenAiApi(textoMarcado);
                            break;
                        case Opciones.LLM_a_Usar.Ollama:
                            respuestaLLM = await CallOllamaApi(textoMarcado);
                            break;
                        case Opciones.LLM_a_Usar.Groq:
                            respuestaLLM = await CallGroqAiApi(textoMarcado);
                            break;
                        case Opciones.LLM_a_Usar.Grok:
                            respuestaLLM = await CallGrokApi(textoMarcado);
                            break;
                        case Opciones.LLM_a_Usar.Claude:
                            respuestaLLM = await CallClaudeApi(textoMarcado);
                            break;
                        default:
                            throw new ArgumentException("LLM no válido");
                    }
                }

                if (string.IsNullOrEmpty(respuestaLLM))
                {
                    throw new ApplicationException("No se recibió respuesta del LLM");
                }

                ProcesarRespuestaLLM(respuestaLLM, respuestaCompleta);
            }
            catch (Exception ex)
            {
                Log.Log($"Error al procesar texto con LLM: {ex.Message}");
                throw;
            }
        }

        private void ProcesarRespuestaLLM(string respuestaLLM, string respuestaCompleta)
        {
            // Para Claude Web Search, la respuesta ya viene en formato de texto final
            if (Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch)
            {
                // Extraer contenido estructurado (textos + enlaces)
                string contenidoEstructurado = ExtraerEnlacesWebSearch(respuestaCompleta);

                if (!string.IsNullOrEmpty(contenidoEstructurado))
                {
                    // Si hay contenido estructurado, usarlo directamente
                    TextoCorregido = $"{TextoACorregir}\n\n## Respuesta:\n{contenidoEstructurado}";
                    Log.Log("Claude Web Search: Usando contenido estructurado con textos y enlaces");
                }
                else
                {
                    // Fallback: intentar extraer JSON primero, si falla usar respuesta completa
                    string respuestaExtraida = ExtraerValorUnicoJSON(respuestaLLM);
                    if (respuestaExtraida == null)
                    {
                        respuestaExtraida = respuestaLLM;
                        Log.Log("Claude Web Search: No se encontró JSON, usando respuesta completa");
                    }

                    respuestaExtraida = System.Text.RegularExpressions.Regex.Replace(respuestaExtraida, @"\*\*(.*?)\*\*", "$1");
                    respuestaExtraida = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraida).Trim('"');
                    respuestaExtraida = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraida);

                    TextoCorregido = $"{TextoACorregir}\n\n{respuestaExtraida}";
                    Log.Log("Claude Web Search: Usando respuesta fallback");
                }

                return;
            }

            // Para otros LLMs, procesamiento normal con JSON requerido
            string respuestaExtraidaNormal = ExtraerValorUnicoJSON(respuestaLLM);
            if (respuestaExtraidaNormal == null)
            {
                throw new ApplicationException("Error al extraer el texto corregido de la respuesta JSON");
            }

            respuestaExtraidaNormal = System.Text.RegularExpressions.Regex.Replace(respuestaExtraidaNormal, @"\*\*(.*?)\*\*", "$1");
            respuestaExtraidaNormal = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraidaNormal).Trim('"');
            respuestaExtraidaNormal = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraidaNormal);

            // Para el modo Pregunta-Respuesta, combinar pregunta original con la respuesta
            if (Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta)
            {
                TextoCorregido = $"{TextoACorregir}\n{respuestaExtraidaNormal}";
            }
            else
            {
                TextoCorregido = respuestaExtraidaNormal;
            }
        }

        private string ExtraerEnlacesWebSearch(string respuestaCompleta)
        {
            try
            {
                if (string.IsNullOrEmpty(respuestaCompleta))
                {
                    Log.Log("ExtraerEnlacesWebSearch: respuestaCompleta está vacía");
                    return "";
                }

                Log.Log($"ExtraerEnlacesWebSearch: Procesando respuesta de {respuestaCompleta.Length} caracteres");

                var contenidoFormateado = new List<string>();

                // Deserializar la respuesta completa para extraer los resultados
                var data = JsonConvert.DeserializeObject<dynamic>(respuestaCompleta);

                if (data?.content != null)
                {
                    Log.Log($"ExtraerEnlacesWebSearch: Encontrados {data.content.Count} elementos de contenido");

                    foreach (var contentItem in data.content)
                    {
                        string tipoElemento = contentItem.type?.ToString() ?? "";
                        Log.Log($"ExtraerEnlacesWebSearch: Procesando elemento tipo: {tipoElemento}");

                        // Procesar elementos de texto con sus citas
                        if (tipoElemento == "text" && contentItem.text != null)
                        {
                            string textoRespuesta = contentItem.text.ToString().Trim();

                            // Solo procesar textos que no sean muy cortos y tengan contenido útil
                            if (!string.IsNullOrWhiteSpace(textoRespuesta) && textoRespuesta.Length > 10)
                            {
                                Log.Log($"ExtraerEnlacesWebSearch: Texto encontrado: {textoRespuesta.Substring(0, Math.Min(50, textoRespuesta.Length))}...");

                                // Agregar el texto principal como viñeta
                                contenidoFormateado.Add($"* {textoRespuesta}");

                                // Verificar si hay citas asociadas
                                if (contentItem.citations != null && contentItem.citations.Count > 0)
                                {
                                    Log.Log($"ExtraerEnlacesWebSearch: Encontradas {contentItem.citations.Count} citas para este texto");

                                    foreach (var citation in contentItem.citations)
                                    {
                                        if (citation.type == "web_search_result_location")
                                        {
                                            string titulo = citation.title?.ToString() ?? "Sin título";
                                            string url = citation.url?.ToString() ?? "";

                                            if (!string.IsNullOrEmpty(url))
                                            {
                                                // Agregar el enlace como sub-viñeta indentada
                                                contenidoFormateado.Add($"  * [{titulo}]({url})");
                                                Log.Log($"ExtraerEnlacesWebSearch: Cita agregada - {titulo}");
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        // También capturar resultados de búsqueda web como fuentes adicionales
                        else if (tipoElemento == "web_search_tool_result" && contentItem.content != null)
                        {
                            Log.Log($"ExtraerEnlacesWebSearch: Procesando resultados de búsqueda web");

                            var enlacesBusqueda = new List<string>();

                            foreach (var searchResult in contentItem.content)
                            {
                                if (searchResult.type == "web_search_result")
                                {
                                    string titulo = searchResult.title?.ToString() ?? "Sin título";
                                    string url = searchResult.url?.ToString() ?? "";
                                    string pageAge = searchResult.page_age?.ToString() ?? "";

                                    if (!string.IsNullOrEmpty(url))
                                    {
                                        string enlaceFormateado;
                                        if (!string.IsNullOrEmpty(pageAge))
                                        {
                                            enlaceFormateado = $"  * [{titulo}]({url}) - {pageAge}";
                                        }
                                        else
                                        {
                                            enlaceFormateado = $"  * [{titulo}]({url})";
                                        }

                                        enlacesBusqueda.Add(enlaceFormateado);
                                    }
                                }
                            }

                            // Si hay resultados de búsqueda adicionales, agregarlos al final
                            if (enlacesBusqueda.Any())
                            {
                                contenidoFormateado.Add("* Fuentes adicionales consultadas:");
                                contenidoFormateado.AddRange(enlacesBusqueda.Take(5)); // Limitar a 5 para no sobrecargar
                            }
                        }
                    }
                }
                else
                {
                    Log.Log("ExtraerEnlacesWebSearch: No se encontró contenido en la respuesta");
                }

                Log.Log($"ExtraerEnlacesWebSearch: Generadas {contenidoFormateado.Count} líneas de contenido formateado");

                if (contenidoFormateado.Any())
                {
                    return string.Join("\n", contenidoFormateado);
                }

                return "";
            }
            catch (Exception ex)
            {
                Log.Log($"Error al extraer enlaces de web search: {ex.Message}");
                return "";
            }
        }

        private async Task SimularCorreccion()
        {
            await Task.Delay(1000);
            TextoCorregido = "Texto simulado de prueba";
            Log.Log("Simulación completada");
        }

        public string ExtraerValorUnicoJSON(string input)
        {
            try
            {
                int startJson = input.IndexOf('{');
                int endJson = input.LastIndexOf('}') + 1;

                if (startJson == -1 || endJson == -1 || endJson <= startJson)
                {
                    Log.Log("Formato JSON inválido en la respuesta");
                    return null;
                }

                string jsonString = input[startJson..endJson];
                JObject jsonObject = JObject.Parse(jsonString);
                var firstField = jsonObject.Properties().FirstOrDefault();

                return firstField?.Value?.ToString();
            }
            catch (JsonException ex)
            {
                Log.Log($"Error al procesar JSON: {ex.Message}");
                return null;
            }
        }

        private string CrearMensajeDeSistema()
        {
            return Opciones.Instance.modo switch
            {
                Opciones.modoDeUso.Corregir =>
                    "You are an engineer working in industrial automation. Your task is to review texts and rewrite them in a simple and concise manner. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear without any modifications. For all other technical terms, write them normally without adding any brackets or special formatting. Preserve any existing markdown language if present. Please rewrite the following text in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }. Important: Do not add any new brackets to words that aren't already enclosed in double brackets.",

                Opciones.modoDeUso.Ortografia =>
                "Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear. For all other words, only correct spelling mistakes while preserving technical terms. Preserve any existing markdown language if present, but do not introduce new markdown styling (such as bold or italics) for emphasis unless it was part of the original input. Please write in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }.",

                Opciones.modoDeUso.PreguntaRespuesta =>
                "You are a helpful assistant specialized in industrial automation and technical topics. Please answer the user's question accurately and clearly. If the question contains words in double brackets [[like this]], preserve them exactly as they appear in your response. Please write your answer in " + IdiomaDetectado + " and respond in the following JSON format: { \"Reply_text\": \"Your answer here\" }.",

                _ => "You are an engineer working specialized in industrial automation. If the question contains words in double brackets [[like this]], preserve them exactly as they appear. Please answer the following question in " + IdiomaDetectado + " and respond in the following JSON format: { \"Reply_text\": \"Your text here\" }."
            };
        }

        private string CrearMensajeDeWebSearch()
        {
            return "You are a helpful assistant specialized in industrial automation and technical topics with web search capabilities. " +
                   "When users ask questions, you can use web search to find current and accurate information to provide comprehensive answers. " +
                   "If the question contains words in double brackets [[like this]], preserve them exactly as they appear in your response. " +
                   "Use the web search tool when you need to find recent information, current data, or specific technical details that might not be in your training data. " +
                   "Always provide well-researched, accurate answers and cite your sources when using web search results. " +
                   "Please write your answer in " + IdiomaDetectado + ". " +
                   "You can respond in JSON format like { \"Reply_text\": \"Your answer here\" } or provide a direct text response.";
        }

        private string CrearMensajeDeUsuario(string texto) =>
            Opciones.Instance.modo switch
            {
                Opciones.modoDeUso.Corregir =>
                    $"Please rewrite and improve the following text to make it clearer and more concise the words inside brackets are technical words: \"{texto}\"",

                Opciones.modoDeUso.Ortografia =>
                    $"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",

                Opciones.modoDeUso.PreguntaRespuesta =>
                    texto, // Para pregunta-respuesta, enviamos el texto directamente como la pregunta

                Opciones.modoDeUso.ClaudeWebSearch =>
                    texto, // Para web search, enviamos la pregunta directamente

                Opciones.modoDeUso.Traducir_a_Ingles =>
                    $"Please check the following text for spelling errors and provide the corrected version tranlated to English. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",

                Opciones.modoDeUso.Traducir_a_Italiano =>
                    $"Please check the following text for spelling errors and provide the corrected version tranlated to Italian. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",

                Opciones.modoDeUso.Traducir_a_Espanol =>
                    $"Please check the following text for spelling errors and provide the corrected version tranlated to Spanish. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",

                Opciones.modoDeUso.Traducir_a_Portugues =>
                    $"Please check the following text for spelling errors and provide the corrected version tranlated to Portuguese. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",

                _ => texto
            };

        private async Task<string> CallGrokApi(string input)
        {
            try
            {
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_grokApiKey}");

                var requestData = new
                {
                    messages = new[]
                    {
                        new { role = "system", content = CrearMensajeDeSistema() },
                        new { role = "user", content = CrearMensajeDeUsuario(input) }
                    },
                    model = "grok-3-mini-fast",
                    stream = false,
                    temperature = 0
                };

                return await EnviarSolicitudLLM("https://api.x.ai/v1/chat/completions", requestData);
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a Grok API: {ex.Message}");
                throw;
            }
        }

        private async Task<string> CallOllamaApi(string input)
        {
            try
            {
                var requestData = new
                {
                    model = "llama3.2:latest",
                    messages = new[]
                    {
                        new { role = "system", content = CrearMensajeDeSistema() },
                        new { role = "user", content = CrearMensajeDeUsuario(input) }
                    },
                    stream = false
                };

                return await EnviarSolicitudLLM("http://127.0.0.1:11434/api/chat", requestData);
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a Ollama API: {ex.Message}");
                throw;
            }
        }

        private async Task<string> CallOpenAiApi(string input)
        {
            try
            {
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_openAiApiKey}");

                var requestData = new
                {
                    model = "gpt-4o-mini",
                    messages = new[]
                    {
                        new { role = "system", content = CrearMensajeDeSistema() },
                        new { role = "user", content = CrearMensajeDeUsuario(input) }
                    }
                };

                return await EnviarSolicitudLLM("https://api.openai.com/v1/chat/completions", requestData);
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a OpenAI API: {ex.Message}");
                throw;
            }
        }

        private async Task<string> CallGroqAiApi(string input)
        {
            try
            {
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_groqApiKey}");

                var requestData = new
                {
                    model = "llama-3.2-3b-preview",
                    messages = new[]
                    {
                        new { role = "system", content = CrearMensajeDeSistema() },
                        new { role = "user", content = CrearMensajeDeUsuario(input) }
                    },
                    max_tokens = 2048,
                    stream = false
                };

                return await EnviarSolicitudLLM("https://api.groq.com/openai/v1/chat/completions", requestData);
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a Groq API: {ex.Message}");
                throw;
            }
        }

        private async Task<string> CallClaudeApi(string input)
        {
            try
            {
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("x-api-key", _claudeApiKey);
                _httpClient.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");

                var requestData = new
                {
                    model = "claude-sonnet-4-20250514",
                    max_tokens = 4096,
                    temperature = 1,
                    system = CrearMensajeDeSistema(),
                    messages = new[]
                    {
                        new { role = "user", content = CrearMensajeDeUsuario(input) }
                    },
                    thinking = new
                    {
                        type = "enabled",
                        budget_tokens = 2048
                    }
                };

                return await EnviarSolicitudLLM("https://api.anthropic.com/v1/messages", requestData);
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a Claude API: {ex.Message}");
                throw;
            }
        }

        private async Task<string> CallClaudeWebSearchApi(string input)
        {
            var resultado = await CallClaudeWebSearchApiCompleto(input);
            return resultado.texto;
        }

        private async Task<(string texto, string respuestaCompleta)> CallClaudeWebSearchApiCompleto(string input)
        {
            try
            {
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("x-api-key", _claudeApiKey);
                _httpClient.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");
                _httpClient.DefaultRequestHeaders.Add("anthropic-beta", "web-search-2025-03-05");

                var requestData = new
                {
                    model = "claude-sonnet-4-20250514",
                    max_tokens = 4166,
                    temperature = 1,
                    system = CrearMensajeDeWebSearch(),
                    messages = new[]
    {
                        new { role = "user", content = input } // Para web search, enviamos la pregunta directamente
                    },
                    tools = new[]
    {
                        new
                        {
                            name = "web_search",
                            type = "web_search_20250305"
                        }
                    },
                    thinking = new
                    {
                        type = "enabled",
                        budget_tokens = 4123
                    }
                };

                var content = new StringContent(
                    JsonConvert.SerializeObject(requestData),
                    Encoding.UTF8,
                    "application/json"
                );

                Log.Log($"Enviando solicitud a https://api.anthropic.com/v1/messages");
                Log.Log($"Datos de solicitud: {JsonConvert.SerializeObject(requestData)}");

                using var response = await _httpClient.PostAsync("https://api.anthropic.com/v1/messages", content);

                var responseContent = await response.Content.ReadAsStringAsync();

                // Para Claude Web Search, filtrar campos encrypted_content largos del log
                string logContent = responseContent;
                if (responseContent.Contains("encrypted_content") || responseContent.Contains("signature"))
                {
                    logContent = FiltrarEncryptedContentParaLog(responseContent);
                }

                Log.Log($"Respuesta recibida: {logContent}");

                if (!response.IsSuccessStatusCode)
                {
                    throw new HttpRequestException(
                        $"Error en la solicitud HTTP: {response.StatusCode} - {responseContent}"
                    );
                }

                var data = JsonConvert.DeserializeObject<dynamic>(responseContent);

                if (data.content != null && data.content.Count > 0)
                {
                    // Buscar el elemento con type = "text" en el array de content
                    foreach (var contentItem in data.content)
                    {
                        if (contentItem.type == "text")
                        {
                            return (contentItem.text.ToString(), responseContent);
                        }
                    }
                    // Si no encuentra un elemento con type="text", usar el primer elemento como fallback
                    if (data.content[0].text != null)
                    {
                        return (data.content[0].text.ToString(), responseContent);
                    }
                }

                throw new ApplicationException("No se encontró contenido en la respuesta de Claude Web Search");
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a Claude Web Search API: {ex.Message}");
                throw;
            }
        }

        private async Task<string> EnviarSolicitudLLM(string endpoint, object requestData)
        {
            try
            {
                var content = new StringContent(
                    JsonConvert.SerializeObject(requestData),
                    Encoding.UTF8,
                    "application/json"
                );

                Log.Log($"Enviando solicitud a {endpoint}");
                Log.Log($"Datos de solicitud: {JsonConvert.SerializeObject(requestData)}");

                using var response = await _httpClient.PostAsync(endpoint, content);

                var responseContent = await response.Content.ReadAsStringAsync();

                // Para Claude Web Search, filtrar campos encrypted_content largos del log
                string logContent = responseContent;
                if (endpoint.Contains("anthropic") && (responseContent.Contains("encrypted_content") || responseContent.Contains("signature")))
                {
                    logContent = FiltrarEncryptedContentParaLog(responseContent);
                }

                Log.Log($"Respuesta recibida: {logContent}");

                if (!response.IsSuccessStatusCode)
                {
                    throw new HttpRequestException(
                        $"Error en la solicitud HTTP: {response.StatusCode} - {responseContent}"
                    );
                }

                var data = JsonConvert.DeserializeObject<dynamic>(responseContent);

                // Manejar diferentes formatos de respuesta según el LLM
                if (endpoint.Contains("ollama"))
                {
                    if (data.done == true && data.message != null)
                    {
                        return data.message.content;
                    }
                    throw new ApplicationException("Formato de respuesta de Ollama inválido");
                }
                else if (endpoint.Contains("anthropic"))
                {
                    if (data.content != null && data.content.Count > 0)
                    {
                        // Buscar el elemento con type = "text" en el array de content
                        foreach (var contentItem in data.content)
                        {
                            if (contentItem.type == "text")
                            {
                                return contentItem.text;
                            }
                        }
                        // Si no encuentra un elemento con type="text", usar el primer elemento como fallback
                        if (data.content[0].text != null)
                        {
                            return data.content[0].text;
                        }
                    }
                    throw new ApplicationException("No se encontró contenido en la respuesta de Claude");
                }
                else // OpenAI, Groq, Grok
                {
                    if (data.choices != null && data.choices.Count > 0)
                    {
                        return data.choices[0].message.content;
                    }
                    throw new ApplicationException("No se encontró contenido en la respuesta del LLM");
                }
            }
            catch (Exception ex)
            {
                Log.Log($"Error al enviar solicitud a {endpoint}: {ex.Message}");
                throw;
            }
        }

        private string FiltrarEncryptedContentParaLog(string responseContent)
        {
            try
            {
                // Usar regex para reemplazar campos encrypted_content largos con un placeholder
                var regex = new System.Text.RegularExpressions.Regex(
                    @"""encrypted_content""\s*:\s*""[^""]{50,}""",
                    System.Text.RegularExpressions.RegexOptions.IgnoreCase
                );

                string filtered = regex.Replace(responseContent, @"""encrypted_content"": ""[CONTENT_FILTERED_FOR_LOG]""");

                // También filtrar encrypted_index si es muy largo
                var regexIndex = new System.Text.RegularExpressions.Regex(
                    @"""encrypted_index""\s*:\s*""[^""]{50,}""",
                    System.Text.RegularExpressions.RegexOptions.IgnoreCase
                );

                filtered = regexIndex.Replace(filtered, @"""encrypted_index"": ""[INDEX_FILTERED_FOR_LOG]""");

                // Filtrar signature si es muy largo
                var regexSignature = new System.Text.RegularExpressions.Regex(
                    @"""signature""\s*:\s*""[^""]{20,}""",
                    System.Text.RegularExpressions.RegexOptions.IgnoreCase
                );

                filtered = regexSignature.Replace(filtered, @"""signature"": ""[SIGNATURE_FILTERED_FOR_LOG]""");

                return filtered;
            }
            catch (Exception ex)
            {
                Log.Log($"Error al filtrar encrypted_content: {ex.Message}");
                // Si hay error al filtrar, devolver contenido original
                return responseContent;
            }
        }

        private bool EsSoloURL(string texto)
        {
            if (string.IsNullOrWhiteSpace(texto))
                return false;

            texto = texto.Trim();

            // Lista de patrones comunes de URL
            string[] patrones = {
                "http://", "https://", "www.", "ftp://", "ftps://",
                ".com", ".net", ".org", ".edu", ".gov", ".mil",
                ".es", ".mx", ".ar", ".cl", ".co", ".pe"
            };

            return patrones.Any(patron => texto.ToLower().Contains(patron));
        }

        /// <summary>
        /// Método de fallback que usa Grok para explicar errores cuando el procesamiento principal falla
        /// </summary>
        /// <param name="originalText">Texto original que se intentó procesar</param>
        /// <param name="errorLogs">Logs de la interacción que falló</param>
        /// <returns>Texto original + explicación del error, o solo el error si Grok también falla</returns>
        public async Task<string> ExplicarErrorConGrok(string originalText, string errorLogs)
        {
            try
            {
                Log.Log("Iniciando fallback con Grok para explicar el error");

                // Verificar que tenemos la API key de Grok
                if (string.IsNullOrEmpty(_grokApiKey))
                {
                    Log.Log("Error fallback: No hay API key de Grok disponible");
                    return GetLastErrorMessage(errorLogs);
                }

                // Preparar el prompt para Grok
                string systemPrompt = "Eres un asistente técnico especializado en explicar errores de software. " +
                    "Analiza los logs de error proporcionados y explica de manera simple y clara qué salió mal y por qué, " +
                    "en español. Sé conciso y útil para el usuario final.";

                string userPrompt = $"¿Podrías explicar qué no ha funcionado y por qué según estos logs?\n\n{errorLogs}";

                // Llamar a Grok específicamente para explicar el error
                string explicacion = await CallGrokForErrorExplanation(systemPrompt, userPrompt);

                if (!string.IsNullOrEmpty(explicacion))
                {
                    Log.Log("Grok proporcionó explicación del error exitosamente");
                    return $"{originalText}\n\nERROR: {explicacion}";
                }
                else
                {
                    Log.Log("Grok no pudo proporcionar explicación del error");
                    return GetLastErrorMessage(errorLogs);
                }
            }
            catch (Exception ex)
            {
                Log.Log($"Error en fallback de explicación con Grok: {ex.Message}");
                return GetLastErrorMessage(errorLogs);
            }
        }

        /// <summary>
        /// Método específico para llamar a Grok para explicaciones de errores
        /// </summary>
        private async Task<string> CallGrokForErrorExplanation(string systemPrompt, string userPrompt)
        {
            try
            {
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_grokApiKey}");

                var requestData = new
                {
                    messages = new[]
                    {
                        new { role = "system", content = systemPrompt },
                        new { role = "user", content = userPrompt }
                    },
                    model = "grok-beta",
                    stream = false,
                    temperature = 0.3, // Baja temperatura para respuestas más consistentes en explicaciones de errores
                    max_tokens = 500 // Limitar tokens para explicaciones concisas
                };

                var content = new StringContent(
                    JsonConvert.SerializeObject(requestData),
                    Encoding.UTF8,
                    "application/json"
                );

                Log.Log("Enviando solicitud de explicación de error a Grok");

                using var response = await _httpClient.PostAsync("https://api.x.ai/v1/chat/completions", content);
                var responseContent = await response.Content.ReadAsStringAsync();

                Log.Log($"Respuesta de Grok para explicación de error: {responseContent}");

                if (!response.IsSuccessStatusCode)
                {
                    Log.Log($"Error en solicitud a Grok para explicación: {response.StatusCode} - {responseContent}");
                    return null;
                }

                var data = JsonConvert.DeserializeObject<dynamic>(responseContent);

                if (data?.choices != null && data.choices.Count > 0)
                {
                    string explanation = data.choices[0].message.content?.ToString();
                    return explanation?.Trim();
                }

                Log.Log("No se encontró contenido en la respuesta de Grok para explicación de error");
                return null;
            }
            catch (Exception ex)
            {
                Log.Log($"Error en llamada a Grok para explicación de error: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// Extrae el último mensaje de error de los logs
        /// </summary>
        private string GetLastErrorMessage(string errorLogs)
        {
            try
            {
                if (string.IsNullOrEmpty(errorLogs))
                    return "Error desconocido";

                // Buscar la última línea que contenga "Error no controlado" o "Error"
                var lines = errorLogs.Split('\n');

                for (int i = lines.Length - 1; i >= 0; i--)
                {
                    if (lines[i].Contains("Error no controlado:") ||
                        lines[i].Contains("Error durante la corrección:") ||
                        lines[i].Contains("Error en CorregirTexto:"))
                    {
                        // Extraer solo la parte del error después de los dos puntos
                        var parts = lines[i].Split(':', 2);
                        if (parts.Length > 1)
                        {
                            return parts[1].Trim();
                        }
                        return lines[i].Trim();
                    }
                }

                // Si no encuentra un error específico, devolver la última línea no vacía
                for (int i = lines.Length - 1; i >= 0; i--)
                {
                    if (!string.IsNullOrWhiteSpace(lines[i]))
                    {
                        return lines[i].Trim();
                    }
                }

                return "Error desconocido";
            }
            catch (Exception ex)
            {
                Log.Log($"Error al extraer último mensaje de error: {ex.Message}");
                return "Error desconocido";
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _httpClient?.Dispose();
                    // El Logger es un singleton, no necesitamos disponerlo aquí
                    // pero podemos registrar que se está cerrando la instancia
                    Log?.Log("gtpask instance disposed");
                }

                _disposed = true;
            }
        }

        ~gtpask()
        {
            Dispose(false);
        }
    }

    // Clase auxiliar para manejar excepciones específicas de la aplicación
    public class LLMException : Exception
    {
        public LLMException(string message) : base(message) { }
        public LLMException(string message, Exception innerException) : base(message, innerException) { }
    }

    // Clase auxiliar para validación
    public static class Validations
    {
        public static void ValidateNotNull(object value, string paramName)
        {
            if (value == null)
            {
                throw new ArgumentNullException(paramName);
            }
        }

        public static void ValidateNotNullOrEmpty(string value, string paramName)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentException("Value cannot be null or empty", paramName);
            }
        }
    }
}