using AuthorBuddy.Web.Data;
using AuthorBuddy.Web.Models;
using Microsoft.EntityFrameworkCore;

namespace AuthorBuddy.Web.Services;

public class SettingsService : ISettingsService
{
    private const string KeyOllamaUrl = "ollama_url";
    private const string KeyRagModel = "ollama_rag_model";
    private const string KeyStyleModel = "ollama_style_model";
    private const string KeySpellingModel = "ollama_spelling_model";
    private const string KeyTheme = "theme";
    private const string KeyLektorPrompt = "ollama_lektor_prompt";
    private const string KeyFactCheckTemplate = "ollama_fact_check";
    private const string KeySpellingPrompt = "ollama_spelling_prompt";

    private readonly IDbContextFactory<AppDbContext> _contextFactory;
    private readonly OllamaSettings _ollamaConfig;
    private int? _currentUserId;
    private int? _currentProjectId;

    public SettingsService(IDbContextFactory<AppDbContext> contextFactory, OllamaSettings ollamaConfig)
    {
        _contextFactory = contextFactory;
        _ollamaConfig = ollamaConfig;
    }

    public int? CurrentProjectId
    {
        get => _currentProjectId;
        set => _currentProjectId = value;
    }

    private async Task<string?> GetSettingAsync(string key, int? userId, int? projectId)
    {
        await using var context = await _contextFactory.CreateDbContextAsync();

        var matches = context.Settings.Where(s =>
            s.Key == key &&
            (s.UserId == userId || s.UserId == null) &&
            (s.ProjectId == projectId || s.ProjectId == null));

        var setting = await matches
            .OrderByDescending(s => s.UserId != null)
            .ThenByDescending(s => s.ProjectId != null)
            .FirstOrDefaultAsync();

        return setting?.Value;
    }

    private async Task<string?> GetAsync(string key)
    {
        return await GetSettingAsync(key, _currentUserId, _currentProjectId);
    }

    private async Task SetAsync(string key, string value)
    {
        await using var context = await _contextFactory.CreateDbContextAsync();

        var existing = await context.Settings
            .Where(s => s.Key == key && s.UserId == _currentUserId && s.ProjectId == _currentProjectId)
            .FirstOrDefaultAsync();

        if (existing != null)
        {
            existing.Value = value;
        }
        else
        {
            context.Settings.Add(new SettingEntity
            {
                Key = key,
                Value = value,
                UserId = _currentUserId,
                ProjectId = _currentProjectId
            });
        }

        await context.SaveChangesAsync();
    }

    public Task<string?> GetOllamaUrlAsync() => GetAsync(KeyOllamaUrl);
    public Task<string?> GetRagModelAsync() => GetAsync(KeyRagModel);
    public Task<string?> GetStyleModelAsync() => GetAsync(KeyStyleModel);
    public Task<string?> GetLektorSystemPromptAsync() => GetAsync(KeyLektorPrompt);
    public Task<string?> GetFactCheckTemplateAsync() => GetAsync(KeyFactCheckTemplate);
    public Task<string?> GetSpellingPromptAsync() => GetAsync(KeySpellingPrompt);
    public Task<string?> GetSpellingModelAsync() => GetAsync(KeySpellingModel);
    public Task<string?> GetThemeAsync() => GetAsync(KeyTheme);

    public async Task SaveOllamaUrlAsync(string url)
    {
        await SetAsync(KeyOllamaUrl, url);
        _ollamaConfig.OllamaUrl = url;
    }

    public async Task SaveRagModelAsync(string model)
    {
        await SetAsync(KeyRagModel, model);
        _ollamaConfig.StyleModel = model;
    }

    public async Task SaveStyleModelAsync(string model)
    {
        await SetAsync(KeyStyleModel, model);
        _ollamaConfig.StyleModel = model;
    }

    public async Task SaveLektorSystemPromptAsync(string prompt)
    {
        await SetAsync(KeyLektorPrompt, prompt);
        _ollamaConfig.LektorSystemPrompt = prompt;
    }

    public async Task SaveFactCheckTemplateAsync(string prompt)
    {
        await SetAsync(KeyFactCheckTemplate, prompt);
        _ollamaConfig.FactCheckTemplate = prompt;
    }

    public async Task SaveSpellingPromptAsync(string prompt)
    {
        await SetAsync(KeySpellingPrompt, prompt);
        _ollamaConfig.SpellingPrompt = prompt;
    }

    public async Task SaveSpellingModelAsync(string model)
    {
        await SetAsync(KeySpellingModel, model);
        _ollamaConfig.SpellingModel = model;
    }

    public Task SaveThemeAsync(string theme)
        => SetAsync(KeyTheme, theme);

    public async Task<List<UserEntity>> GetUsersAsync()
    {
        await using var context = await _contextFactory.CreateDbContextAsync();
        return await context.Users.ToListAsync();
    }

    public async Task<UserEntity> GetCurrentUserAsync()
    {
        await using var context = await _contextFactory.CreateDbContextAsync();
        var user = await context.Users.FirstOrDefaultAsync(u => u.IsCurrent);
        return user ?? await context.Users.FirstAsync();
    }

    public async Task SetCurrentUserAsync(int userId)
    {
        await using var context = await _contextFactory.CreateDbContextAsync();
        var users = await context.Users.ToListAsync();
        foreach (var u in users)
            u.IsCurrent = u.Id == userId;
        await context.SaveChangesAsync();
        _currentUserId = userId;
    }

    public async Task<UserEntity> CreateUserAsync(string name)
    {
        await using var context = await _contextFactory.CreateDbContextAsync();
        var user = new UserEntity { Name = name };
        context.Users.Add(user);
        await context.SaveChangesAsync();
        return user;
    }

    public async Task InitializeAsync()
    {
        await using var context = await _contextFactory.CreateDbContextAsync();

        var userExists = await context.Users.AnyAsync();
        if (!userExists)
        {
            var defaultUser = new UserEntity { Name = "Standardbenutzer", IsCurrent = true };
            context.Users.Add(defaultUser);
            await context.SaveChangesAsync();
            _currentUserId = defaultUser.Id;
        }
        else
        {
            var current = await context.Users.FirstOrDefaultAsync(u => u.IsCurrent);
            _currentUserId = current?.Id ?? (await context.Users.FirstAsync()).Id;
        }

        var entries = new (string Key, string Default)[]
        {
            (KeyOllamaUrl, _ollamaConfig.OllamaUrl),
            (KeyRagModel, _ollamaConfig.StyleModel),
            (KeyStyleModel, _ollamaConfig.StyleModel),
            (KeySpellingModel, _ollamaConfig.SpellingModel),
            (KeyTheme, _ollamaConfig.Theme),
            (KeyLektorPrompt, _ollamaConfig.LektorSystemPrompt),
            (KeyFactCheckTemplate, _ollamaConfig.FactCheckTemplate),
            (KeySpellingPrompt, _ollamaConfig.SpellingPrompt),
        };

        foreach (var entry in entries)
        {
            var existing = await context.Settings
                .Where(s => s.Key == entry.Key && s.UserId == _currentUserId && s.ProjectId == null)
                .FirstOrDefaultAsync();

            if (existing != null)
            {
                var val = existing.Value;
                if (entry.Key == KeyOllamaUrl) _ollamaConfig.OllamaUrl = val;
                else if (entry.Key == KeyRagModel) _ollamaConfig.StyleModel = val;
                else if (entry.Key == KeyStyleModel) _ollamaConfig.StyleModel = val;
                else if (entry.Key == KeySpellingModel) _ollamaConfig.SpellingModel = val;
                else if (entry.Key == KeyTheme) _ollamaConfig.Theme = val;
                else if (entry.Key == KeyLektorPrompt) _ollamaConfig.LektorSystemPrompt = val;
                else if (entry.Key == KeyFactCheckTemplate) _ollamaConfig.FactCheckTemplate = val;
                else if (entry.Key == KeySpellingPrompt) _ollamaConfig.SpellingPrompt = val;
            }
            else
            {
                context.Settings.Add(new SettingEntity
                {
                    Key = entry.Key,
                    Value = entry.Default,
                    UserId = _currentUserId,
                    ProjectId = null
                });
            }
        }

        await context.SaveChangesAsync();
    }
}
