using AuthorBuddy.Web.Data;
using AuthorBuddy.Web.Models;
using LiteDB;

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 const string KeyAdvocatusDiaboli = "ollama_advocatus_diaboli";
    private const string KeyHookFinder = "ollama_hook_finder";
    private const string KeyEli5 = "ollama_eli5";
    private const string KeyLanguage = "language";

    private const string KeyLastProjectId = "last_project_id";

    private readonly ILiteDatabase _db;
    private readonly OllamaSettings _ollamaConfig;
    private int? _currentUserId;
    private int? _currentProjectId;

    public SettingsService(ILiteDatabase db, OllamaSettings ollamaConfig)
    {
        _db = db;
        _ollamaConfig = ollamaConfig;
    }

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

    public string? CurrentFilePath { get; set; }
    public string? PendingAssistantInput { get; set; }
    public string? PendingAssistantOutput { get; set; }

    public Task<int?> GetLastProjectIdAsync()
    {
        var col = _db.GetCollection<SettingEntity>("settings");
        var setting = col.FindOne(s => s.Key == KeyLastProjectId && s.UserId == _currentUserId && s.ProjectId == null);
        if (setting != null && int.TryParse(setting.Value, out var id))
            return Task.FromResult<int?>(id);
        return Task.FromResult<int?>(null);
    }

    public Task SaveLastProjectIdAsync(int projectId)
    {
        var col = _db.GetCollection<SettingEntity>("settings");
        var existing = col.FindOne(s => s.Key == KeyLastProjectId && s.UserId == _currentUserId && s.ProjectId == null);
        if (existing != null)
        {
            existing.Value = projectId.ToString();
            col.Update(existing);
        }
        else
        {
            col.Insert(new SettingEntity
            {
                Key = KeyLastProjectId,
                Value = projectId.ToString(),
                UserId = _currentUserId,
                ProjectId = null
            });
        }
        return Task.CompletedTask;
    }

    private string? GetSetting(string key, int? userId, int? projectId)
    {
        var col = _db.GetCollection<SettingEntity>("settings");

        var matches = col.Find(s =>
            s.Key == key &&
            (s.UserId == userId || s.UserId == null) &&
            (s.ProjectId == projectId || s.ProjectId == null));

        return matches
            .OrderByDescending(s => s.UserId != null)
            .ThenByDescending(s => s.ProjectId != null)
            .FirstOrDefault()?.Value;
    }

    private string? Get(string key)
    {
        return GetSetting(key, _currentUserId, _currentProjectId);
    }

    private void Set(string key, string value)
    {
        var col = _db.GetCollection<SettingEntity>("settings");

        var existing = col.FindOne(s =>
            s.Key == key && s.UserId == _currentUserId && s.ProjectId == null);

        if (existing != null)
        {
            existing.Value = value;
            col.Update(existing);
        }
        else
        {
            col.Insert(new SettingEntity
            {
                Key = key,
                Value = value,
                UserId = _currentUserId,
                ProjectId = null
            });
        }
    }

    public Task<string?> GetOllamaUrlAsync() => Task.FromResult(Get(KeyOllamaUrl));
    public Task<string?> GetRagModelAsync() => Task.FromResult(Get(KeyRagModel));
    public Task<string?> GetStyleModelAsync() => Task.FromResult(Get(KeyStyleModel));
    public Task<string?> GetLektorSystemPromptAsync() => Task.FromResult(Get(KeyLektorPrompt));
    public Task<string?> GetFactCheckTemplateAsync() => Task.FromResult(Get(KeyFactCheckTemplate));
    public Task<string?> GetSpellingPromptAsync() => Task.FromResult(Get(KeySpellingPrompt));
    public Task<string?> GetSpellingModelAsync() => Task.FromResult(Get(KeySpellingModel));
    public Task<string?> GetAdvocatusDiaboliPromptAsync() => Task.FromResult(Get(KeyAdvocatusDiaboli));
    public Task<string?> GetHookFinderPromptAsync() => Task.FromResult(Get(KeyHookFinder));
    public Task<string?> GetEli5PromptAsync() => Task.FromResult(Get(KeyEli5));
    public Task<string?> GetThemeAsync() => Task.FromResult(Get(KeyTheme));
    public Task<string?> GetLanguageAsync() => Task.FromResult(Get(KeyLanguage));
    public Task SaveLanguageAsync(string language)
    {
        Set(KeyLanguage, language);
        _ollamaConfig.Language = language;
        return Task.CompletedTask;
    }

    public Task SaveOllamaUrlAsync(string url)
    {
        Set(KeyOllamaUrl, url);
        _ollamaConfig.OllamaUrl = url;
        return Task.CompletedTask;
    }

    public Task SaveRagModelAsync(string model)
    {
        Set(KeyRagModel, model);
        _ollamaConfig.EmbedModel = model;
        return Task.CompletedTask;
    }

    public Task SaveStyleModelAsync(string model)
    {
        Set(KeyStyleModel, model);
        _ollamaConfig.StyleModel = model;
        return Task.CompletedTask;
    }

    public Task SaveLektorSystemPromptAsync(string prompt)
    {
        Set(KeyLektorPrompt, prompt);
        _ollamaConfig.LektorSystemPrompt = prompt;
        return Task.CompletedTask;
    }

    public Task SaveFactCheckTemplateAsync(string prompt)
    {
        Set(KeyFactCheckTemplate, prompt);
        _ollamaConfig.FactCheckTemplate = prompt;
        return Task.CompletedTask;
    }

    public Task SaveSpellingPromptAsync(string prompt)
    {
        Set(KeySpellingPrompt, prompt);
        _ollamaConfig.SpellingPrompt = prompt;
        return Task.CompletedTask;
    }

    public Task SaveSpellingModelAsync(string model)
    {
        Set(KeySpellingModel, model);
        _ollamaConfig.SpellingModel = model;
        return Task.CompletedTask;
    }

    public Task SaveAdvocatusDiaboliPromptAsync(string prompt)
    {
        Set(KeyAdvocatusDiaboli, prompt);
        _ollamaConfig.AdvocatusDiaboliPrompt = prompt;
        return Task.CompletedTask;
    }

    public Task SaveHookFinderPromptAsync(string prompt)
    {
        Set(KeyHookFinder, prompt);
        _ollamaConfig.HookFinderPrompt = prompt;
        return Task.CompletedTask;
    }

    public Task SaveEli5PromptAsync(string prompt)
    {
        Set(KeyEli5, prompt);
        _ollamaConfig.Eli5Prompt = prompt;
        return Task.CompletedTask;
    }

    public Task SaveThemeAsync(string theme)
    {
        Set(KeyTheme, theme);
        return Task.CompletedTask;
    }

    public Task<List<UserEntity>> GetUsersAsync()
    {
        var col = _db.GetCollection<UserEntity>("users");
        return Task.FromResult(col.FindAll().ToList());
    }

    public Task<UserEntity> GetCurrentUserAsync()
    {
        var col = _db.GetCollection<UserEntity>("users");
        if (_currentUserId != null)
        {
            var user = col.FindById(_currentUserId.Value);
            if (user != null) return Task.FromResult(user);
        }
        return Task.FromResult(col.FindAll().FirstOrDefault() ?? new UserEntity());
    }

    public void SetCurrentUser(int userId)
    {
        _currentUserId = userId;
    }

    public Task<UserEntity> CreateUserAsync(string name)
    {
        var col = _db.GetCollection<UserEntity>("users");
        var hasUsers = col.FindAll().Any();
        var user = new UserEntity
        {
            Name = name,
            Role = hasUsers ? UserRole.Author : UserRole.Administrator
        };
        col.Insert(user);
        return Task.FromResult(user);
    }

    private void MigrateSchema()
    {
        const string schemaKey = "_schema_version";
        var settings = _db.GetCollection<SettingEntity>("settings");
        if (settings.FindOne(s => s.Key == schemaKey && s.UserId == null && s.ProjectId == null) != null)
            return;

        var users = _db.GetCollection<UserEntity>("users");
        var all = users.FindAll().OrderBy(u => u.Id).ToList();
        if (all.Count > 0)
        {
            all[0].Role = UserRole.Administrator;
            foreach (var u in all)
            {
                u.IsActive = true;
                users.Update(u);
            }
        }

        var authorExists = all.Any(u => u.Name == "Author");
        if (!authorExists)
        {
            var authorUser = new UserEntity
            {
                Name = "Author",
                PasswordHash = TotpHelper.HashPassword("author123"),
                TwoFactorEnabled = false,
                TwoFactorSecret = string.Empty,
                Role = UserRole.Author,
                IsActive = true
            };
            users.Insert(authorUser);
        }

        settings.Insert(new SettingEntity
        {
            Key = schemaKey,
            Value = "1",
            UserId = null,
            ProjectId = null
        });
    }

    public Task LoadProjectSettingsAsync()
    {
        var settings = _db.GetCollection<SettingEntity>("settings");
        var entries = new (string Key, Action<string> Setter)[]
        {
            (KeyOllamaUrl, val => _ollamaConfig.OllamaUrl = val),
            (KeyRagModel, val => _ollamaConfig.EmbedModel = val),
            (KeyStyleModel, val => _ollamaConfig.StyleModel = val),
            (KeySpellingModel, val => _ollamaConfig.SpellingModel = val),
            (KeyTheme, val => _ollamaConfig.Theme = val),
            (KeyLektorPrompt, val => _ollamaConfig.LektorSystemPrompt = val),
            (KeyFactCheckTemplate, val => _ollamaConfig.FactCheckTemplate = val),
            (KeySpellingPrompt, val => _ollamaConfig.SpellingPrompt = val),
            (KeyAdvocatusDiaboli, val => _ollamaConfig.AdvocatusDiaboliPrompt = val),
            (KeyHookFinder, val => _ollamaConfig.HookFinderPrompt = val),
            (KeyEli5, val => _ollamaConfig.Eli5Prompt = val),
            (KeyLanguage, val => _ollamaConfig.Language = val),
        };

        foreach (var entry in entries)
        {
            var val = Get(entry.Key);
            if (val != null) entry.Setter(val);
        }

        return Task.CompletedTask;
    }

    public Task InitializeAsync()
    {
        MigrateSchema();

        if (_currentUserId == null)
        {
            var firstUser = _db.GetCollection<UserEntity>("users").FindAll().FirstOrDefault();
            if (firstUser == null) return Task.CompletedTask;
            _currentUserId = firstUser.Id;
        }

        var settings = _db.GetCollection<SettingEntity>("settings");

        var lang = _ollamaConfig.Language;

        var entries = new (string Key, string Default)[]
        {
            (KeyOllamaUrl, _ollamaConfig.OllamaUrl),
            (KeyRagModel, _ollamaConfig.StyleModel),
            (KeyStyleModel, _ollamaConfig.StyleModel),
            (KeySpellingModel, _ollamaConfig.SpellingModel),
            (KeyTheme, _ollamaConfig.Theme),
            (KeyLanguage, lang),
            (KeyLektorPrompt, lang == "en" ? _ollamaConfig.LektorSystemPromptEn : _ollamaConfig.LektorSystemPrompt),
            (KeyFactCheckTemplate, lang == "en" ? _ollamaConfig.FactCheckTemplateEn : _ollamaConfig.FactCheckTemplate),
            (KeySpellingPrompt, lang == "en" ? _ollamaConfig.SpellingPromptEn : _ollamaConfig.SpellingPrompt),
            (KeyAdvocatusDiaboli, lang == "en" ? _ollamaConfig.AdvocatusDiaboliPromptEn : _ollamaConfig.AdvocatusDiaboliPrompt),
            (KeyHookFinder, lang == "en" ? _ollamaConfig.HookFinderPromptEn : _ollamaConfig.HookFinderPrompt),
            (KeyEli5, lang == "en" ? _ollamaConfig.Eli5PromptEn : _ollamaConfig.Eli5Prompt),
        };

        foreach (var entry in entries)
        {
            var existing = settings.FindOne(s =>
                s.Key == entry.Key && s.UserId == _currentUserId && s.ProjectId == _currentProjectId);

            if (existing != null)
            {
                var val = existing.Value;
                if (entry.Key == KeyOllamaUrl) _ollamaConfig.OllamaUrl = val;
                else if (entry.Key == KeyRagModel) _ollamaConfig.EmbedModel = 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 == KeyLanguage) _ollamaConfig.Language = 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 if (entry.Key == KeyAdvocatusDiaboli) _ollamaConfig.AdvocatusDiaboliPrompt = val;
                else if (entry.Key == KeyHookFinder) _ollamaConfig.HookFinderPrompt = val;
                else if (entry.Key == KeyEli5) _ollamaConfig.Eli5Prompt = val;
            }
            else
            {
                settings.Insert(new SettingEntity
                {
                    Key = entry.Key,
                    Value = entry.Default,
                    UserId = _currentUserId,
                    ProjectId = _currentProjectId
                });
            }
        }

        return Task.CompletedTask;
    }
}
