using AuthorBuddy.Web.Models;
using System.Text.Json;
using static System.Net.Mime.MediaTypeNames;

namespace AuthorBuddy.Web.Services;

public class OllamaService : IOllamaService
{
    private readonly HttpClient _httpClient;
    private readonly OllamaSettings _settings;
    private string _baseUrl = "http://localhost:11434";

    public OllamaService(OllamaSettings settings)
    {
        _settings = settings;
        _baseUrl = settings.OllamaUrl;
        _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(300) };
    }

    public void Reconfigure(string url)
    {
        _baseUrl = url;
    }

    public async Task<bool> TestEmbeddingAsync(string model)
    {
        try
        {
            var request = new { model = model, prompt = "test" };
            var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}/api/embeddings", request);
            if (!response.IsSuccessStatusCode) return false;

            var content = await response.Content.ReadAsStringAsync();
            using var doc = JsonDocument.Parse(content);
            if (doc.RootElement.TryGetProperty("embedding", out var embedding))
                return embedding.EnumerateArray().Any();

            return false;
        }
        catch
        {
            return false;
        }
    }

    public async Task<float[]> GetEmbeddingAsync(string text)
    {
        try
        {
            var request = new { model = _settings.EmbedModel, prompt = text };
            var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}/api/embeddings", request);
            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(content);
                if (doc.RootElement.TryGetProperty("embedding", out var embedding))
                {
                    var list = new List<float>();
                    foreach (var val in embedding.EnumerateArray())
                        list.Add(val.GetSingle());
                    return list.ToArray();
                }
            }
            return Array.Empty<float>();
        }
        catch
        {
            return Array.Empty<float>();
        }
    }

    public async Task<IEnumerable<LLM_Model>> GetLocalModelsAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/api/tags");
            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(content);
                var root = doc.RootElement;

                if (root.TryGetProperty("models", out var models))
                {
                    var modelList = new List<LLM_Model>();
                    foreach (var model in models.EnumerateArray())
                    {
                        modelList.Add(new LLM_Model(model));

          
                    }
                    return modelList;
                }
            }
            return new List<LLM_Model>();
        }
        catch (Exception)
        {
            return new List<LLM_Model>();
        }
    }

    public async Task<string> QuickSpellCheckAsync(string text)
    {
        return await Chat(_settings.SpellingModel, text, _settings.SpellingPrompt);
    }

    public async Task<string> StreamStyleCheckAsync(string text)
    {
        var systemPrompt = _settings.LektorSystemPrompt;
        return await Chat(_settings.StyleModel, text, systemPrompt);

    }

    public async Task<string> AnswerBasedOnFactsAsync(string question, string facts)
    {
        var prompt = _settings.GetFactCheckPrompt(question, facts);

        return await Chat(_settings.EmbedModel, prompt);
    }



    public async Task<string> Chat(string model, string userMessage, string? systemPrompt = null)
    {
        try
        {
            var request = new
            {
                model = model,
                prompt = userMessage,
                system = systemPrompt,
                stream = false
            };

            var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}/api/generate", request);
            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(content);
                var root = doc.RootElement;

                if (root.TryGetProperty("response", out var responseText))
                {
                    return responseText.GetString() ?? "";
                }
            }
            return "Fehler bei der Verarbeitung.";
        }
        catch (Exception ex)
        {
            return $"Error: {ex.Message}";
        }
    }

    public async IAsyncEnumerable<string> ChatStream(string model, string userMessage, string? systemPrompt = null)
    {
        var request = new
        {
            model = model,
            prompt = userMessage,
            stream = true
        };

        HttpResponseMessage? response = null;
        try
        {
            response = await _httpClient.PostAsync(
                $"{_baseUrl}/api/generate",
                new StringContent(JsonSerializer.Serialize(request), System.Text.Encoding.UTF8, "application/json")
            );

            if (response.IsSuccessStatusCode)
            {
                using var stream = await response.Content.ReadAsStreamAsync();
                using var reader = new StreamReader(stream);

                string? line;
                while ((line = await reader.ReadLineAsync()) != null)
                {
                    var text = ExtractResponseText(line);
                    if (!string.IsNullOrEmpty(text))
                    {
                        yield return text;
                    }
                }
            }
        }
        finally
        {
            response?.Dispose();
        }
    }

    private string ExtractResponseText(string jsonLine)
    {
        if (string.IsNullOrWhiteSpace(jsonLine))
            return string.Empty;

        try
        {
            using var doc = JsonDocument.Parse(jsonLine);
            var root = doc.RootElement;

            if (root.TryGetProperty("response", out var responseText))
            {
                return responseText.GetString() ?? string.Empty;
            }
        }
        catch { }

        return string.Empty;
    }

    public async Task<bool> DeleteModel(string modelName)
    {
        try
        {
            var request = new HttpRequestMessage(HttpMethod.Delete, $"{_baseUrl}/api/delete");
            request.Content = new StringContent(JsonSerializer.Serialize(new { name = modelName }),
                System.Text.Encoding.UTF8, "application/json");

            var response = await _httpClient.SendAsync(request);
            return response.IsSuccessStatusCode;
        }
        catch (Exception)
        {
            return false;
        }
    }

    public async Task<bool> PullModelAsync(string modelName, IProgress<string> progress)
    {
        try
        {
            var request = new { name = modelName, stream = true };
            var response = await _httpClient.PostAsync(
                $"{_baseUrl}/api/pull",
                new StringContent(JsonSerializer.Serialize(request), System.Text.Encoding.UTF8, "application/json")
            );

            if (response.IsSuccessStatusCode)
            {
                using var stream = await response.Content.ReadAsStreamAsync();
                using var reader = new StreamReader(stream);

                string? line;
                while ((line = await reader.ReadLineAsync()) != null)
                {
                    if (string.IsNullOrWhiteSpace(line)) continue;

                    try
                    {
                        using var doc = JsonDocument.Parse(line);
                        var root = doc.RootElement;

                        if (root.TryGetProperty("status", out var status))
                        {
                            var statusText = status.GetString() ?? "";
                            var msg = statusText;

                            if (root.TryGetProperty("completed", out var completed) &&
                                root.TryGetProperty("total", out var total))
                            {
                                var comp = completed.GetInt64();
                                var tot = total.GetInt64();
                                if (tot > 0)
                                {
                                    var pct = (int)(comp * 100 / tot);
                                    msg = $"{statusText}: {comp}/{tot} ({pct}%)";
                                }
                            }

                            progress?.Report(msg);

                            if (statusText == "success")
                                return true;
                        }
                    }
                    catch { }
                }
            }

            return false;
        }
        catch (Exception)
        {
            return false;
        }
    }
}
