using System.IO;
using System.Text.Json;
using NexusShell.Models;

namespace NexusShell.Services;

public sealed class NexusInstalledAppsService
{
    private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };

    private readonly string _storeDir;
    private readonly string _installedPath;
    private InstalledStoreDoc _doc = new();

    public NexusInstalledAppsService()
    {
        _storeDir = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "NinjaNexus",
            "store");
        _installedPath = Path.Combine(_storeDir, "installed.json");
        Load();
    }

    public event EventHandler? InstalledStateChanged;

    public IReadOnlyList<InstalledPackageRecord> GetInstalledRecords() => _doc.Packages;

    public IReadOnlyList<string> GetFavorites() => _doc.Favorites;

    public IReadOnlyList<NexusStoreHistoryEntry> GetHistory() => _doc.History;

    public IReadOnlyList<string> GetActiveDownloads() => _doc.ActiveDownloads;

    public string? GetInstalledVersion(string packageId)
    {
        var record = _doc.Packages.FirstOrDefault(p =>
            string.Equals(p.Id, packageId, StringComparison.OrdinalIgnoreCase));
        return record?.InstalledVersion;
    }

    public bool IsFavorite(string packageId) =>
        _doc.Favorites.Any(id => string.Equals(id, packageId, StringComparison.OrdinalIgnoreCase));

    public void Install(string packageId, string packageName, string version)
    {
        var existing = _doc.Packages.FirstOrDefault(p =>
            string.Equals(p.Id, packageId, StringComparison.OrdinalIgnoreCase));
        if (existing is null)
        {
            _doc.Packages.Add(new InstalledPackageRecord
            {
                Id = packageId,
                InstalledVersion = version,
                InstalledAt = DateTimeOffset.UtcNow,
            });
        }
        else
        {
            existing.InstalledVersion = version;
            existing.InstalledAt = DateTimeOffset.UtcNow;
        }

        AddHistory(packageId, packageName, "Installeret");
        _doc.ActiveDownloads.RemoveAll(id => string.Equals(id, packageId, StringComparison.OrdinalIgnoreCase));
        Save();
        InstalledStateChanged?.Invoke(this, EventArgs.Empty);
    }

    public void Update(string packageId, string packageName, string version)
    {
        Install(packageId, packageName, version);
        AddHistory(packageId, packageName, "Opdateret");
        Save();
        InstalledStateChanged?.Invoke(this, EventArgs.Empty);
    }

    public void Uninstall(string packageId, string packageName)
    {
        _doc.Packages.RemoveAll(p => string.Equals(p.Id, packageId, StringComparison.OrdinalIgnoreCase));
        _doc.Favorites.RemoveAll(id => string.Equals(id, packageId, StringComparison.OrdinalIgnoreCase));
        AddHistory(packageId, packageName, "Afinstalleret");
        Save();
        InstalledStateChanged?.Invoke(this, EventArgs.Empty);
    }

    public void ToggleFavorite(string packageId)
    {
        if (IsFavorite(packageId))
        {
            _doc.Favorites.RemoveAll(id => string.Equals(id, packageId, StringComparison.OrdinalIgnoreCase));
        }
        else
        {
            _doc.Favorites.Add(packageId);
        }

        Save();
        InstalledStateChanged?.Invoke(this, EventArgs.Empty);
    }

    public void QueueDownload(string packageId)
    {
        if (!_doc.ActiveDownloads.Any(id => string.Equals(id, packageId, StringComparison.OrdinalIgnoreCase)))
        {
            _doc.ActiveDownloads.Add(packageId);
            Save();
            InstalledStateChanged?.Invoke(this, EventArgs.Empty);
        }
    }

    private void AddHistory(string packageId, string packageName, string action)
    {
        _doc.History.Insert(0, new NexusStoreHistoryEntry
        {
            PackageId = packageId,
            PackageName = packageName,
            Action = action,
            At = DateTimeOffset.UtcNow,
        });

        if (_doc.History.Count > 50)
        {
            _doc.History.RemoveRange(50, _doc.History.Count - 50);
        }
    }

    private void Load()
    {
        Directory.CreateDirectory(_storeDir);
        if (!File.Exists(_installedPath))
        {
            _doc = CreateDefaultDoc();
            Save();
            return;
        }

        try
        {
            var json = File.ReadAllText(_installedPath);
            _doc = JsonSerializer.Deserialize<InstalledStoreDoc>(json, JsonOptions) ?? new InstalledStoreDoc();
        }
        catch
        {
            _doc = CreateDefaultDoc();
            Save();
        }
    }

    private static InstalledStoreDoc CreateDefaultDoc() => new()
    {
        Packages =
        [
            new InstalledPackageRecord
            {
                Id = "nexus-files",
                InstalledVersion = "1.0.0",
                InstalledAt = DateTimeOffset.UtcNow.AddDays(-14),
            },
            new InstalledPackageRecord
            {
                Id = "nexus-browser",
                InstalledVersion = "1.0.0",
                InstalledAt = DateTimeOffset.UtcNow.AddDays(-7),
            },
        ],
        History =
        [
            new NexusStoreHistoryEntry
            {
                PackageId = "nexus-files",
                PackageName = "Nexus Files",
                Action = "Installeret",
                At = DateTimeOffset.UtcNow.AddDays(-14),
            },
        ],
    };

    private void Save()
    {
        Directory.CreateDirectory(_storeDir);
        var json = JsonSerializer.Serialize(_doc, JsonOptions);
        File.WriteAllText(_installedPath, json);
    }

    public sealed class InstalledPackageRecord
    {
        public string Id { get; set; } = string.Empty;
        public string InstalledVersion { get; set; } = string.Empty;
        public DateTimeOffset InstalledAt { get; set; }
    }

    private sealed class InstalledStoreDoc
    {
        public List<InstalledPackageRecord> Packages { get; set; } = [];
        public List<string> Favorites { get; set; } = [];
        public List<NexusStoreHistoryEntry> History { get; set; } = [];
        public List<string> ActiveDownloads { get; set; } = [];
    }
}
