using AuthorBuddy.Web.Models;

namespace AuthorBuddy.Web.Services;

public class OllamaService : IOllamaService
{
    private readonly IEnumerable<ILLMBackend> _backends;
    private readonly OllamaSettings _settings;
    private readonly OperationState _opState;

    public OllamaService(IEnumerable<ILLMBackend> backends, OllamaSettings settings, OperationState opState)
    {
        _backends = backends;
        _settings = settings;
        _opState = opState;
    }

    private ILLMBackend ActiveBackend =>
        _backends.FirstOrDefault(b => b.Name == _settings.Backend) ?? _backends.First();

    public void Reconfigure(string url)
    {
        _settings.OllamaUrl = url;
    }

    public async Task<(bool Success, long LatencyMs)> TestConnectionAsync()
    {
        _opState.SetThought($"Teste Verbindung zu {ActiveBackend.Name}...");
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var result = await ActiveBackend.TestConnectionAsync();
        sw.Stop();
        _opState.AppendThought(result ? " OK" : " Fehlgeschlagen");
        return (result, result ? sw.ElapsedMilliseconds : 0);
    }

    public async Task<IEnumerable<LLM_Model>> GetLocalModelsAsync()
    {
        _opState.SetThought($"Lade Modelle von {ActiveBackend.Name}...");
        var models = await ActiveBackend.GetModelsAsync();
        _opState.AppendThought($" {models.Count()} gefunden");
        return models;
    }

    public async Task<bool> TestEmbeddingAsync(string model)
    {
        return ActiveBackend.TestEmbedding(model);
    }

    public async Task<float[]> GetEmbeddingAsync(string text)
    {
        return await ActiveBackend.GetEmbeddingAsync(text, _settings.EmbedModel);
    }

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

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

    public async Task<string> AnswerBasedOnFactsAsync(string question, string facts)
    {
        var prompt = _settings.GetFactCheckPrompt(question, facts);
        return await Chat(_settings.EmbedModel, prompt, temperature: _settings.FactCheckTemp);
    }

    public async Task<bool> ModelExistsAsync(string model)
    {
        return await ActiveBackend.ModelExistsAsync(model);
    }

    public async Task<(bool Success, int ContextWindow)> GetModelContextWindowAsync(string model)
    {
        return await ActiveBackend.GetModelContextWindowAsync(model);
    }

    public async Task<long> MeasureLatencyAsync(string model)
    {
        return await ActiveBackend.MeasureLatencyAsync(model);
    }

    public async Task<string> Chat(string model, string userMessage, string? systemPrompt = null, double? temperature = null)
    {
        _opState.SetThought($"{ActiveBackend.Name}: Sende Anfrage an {model}...");
        var result = await ActiveBackend.ChatAsync(model, userMessage, systemPrompt, temperature);
        _opState.AppendThought(" Fertig");
        return result;
    }

    public async Task ChatStream(string model, string userMessage, Func<string, Task> onToken, string? systemPrompt = null, double? temperature = null)
    {
        _opState.SetThought($"{ActiveBackend.Name}: Verbinde mit Modell {model}...");

        int tokenCount = 0;
        await ActiveBackend.ChatStreamAsync(model, userMessage, async chunk =>
        {
            tokenCount++;
            _opState.AppendThought(chunk);
            await onToken(chunk);
        }, systemPrompt, temperature);

        _opState.AppendThought($" Fertig ({tokenCount} Token)");
    }

    public async Task<bool> PullModelAsync(string modelName, IProgress<string> progress)
    {
        _opState.SetThought($"Lade Modell {modelName}...");
        var result = await ActiveBackend.PullModelAsync(modelName, progress);
        _opState.AppendThought(result ? " Erfolgreich" : " Fehlgeschlagen");
        return result;
    }
}
