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

namespace NexusShell.Services;

public sealed class NexusDefaultAppsService
{
    private readonly string _configPath;
    private NexusDefaultAppsConfig _config;

    public NexusDefaultAppsService()
    {
        var folder = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "NinjaNexus");
        Directory.CreateDirectory(folder);
        _configPath = Path.Combine(folder, "default-apps.json");
        _config = LoadInternal();
    }

    public event EventHandler? PreferencesChanged;

    public IReadOnlyList<NexusDefaultAppPreferenceRow> GetPreferenceRows()
    {
        var rows = new List<NexusDefaultAppPreferenceRow>();
        foreach (var category in NexusFileAssociations.GetCategories())
        {
            rows.Add(new NexusDefaultAppPreferenceRow
            {
                CategoryId = category.Id,
                CategoryLabel = category.Label,
                ExtensionsLabel = category.ExtensionsLabel,
                SelectedApp = GetCategoryHandler(category.Id, category.DefaultApp),
            });
        }

        return rows;
    }

    public NexusDefaultAppId ResolveHandler(string filePath)
    {
        var extension = Path.GetExtension(filePath);
        if (string.IsNullOrWhiteSpace(extension))
        {
            return NexusDefaultAppId.FilesPreview;
        }

        var normalized = extension.ToLowerInvariant();
        if (_config.ExtensionOverrides.TryGetValue(normalized, out var overrideValue) &&
            TryParseAppId(overrideValue, out var overrideApp))
        {
            return overrideApp;
        }

        var categoryId = NexusFileAssociations.GetCategoryIdForExtension(normalized);
        if (categoryId is not null &&
            _config.CategoryHandlers.TryGetValue(categoryId, out var categoryValue) &&
            TryParseAppId(categoryValue, out var categoryApp))
        {
            return categoryApp;
        }

        return NexusFileAssociations.GetBuiltInHandler(normalized);
    }

    public void SavePreferences(IReadOnlyList<NexusDefaultAppPreferenceRow> rows)
    {
        _config.CategoryHandlers.Clear();
        foreach (var row in rows)
        {
            _config.CategoryHandlers[row.CategoryId] = ToStorageValue(row.SelectedApp);
        }

        SaveInternal(_config);
        PreferencesChanged?.Invoke(this, EventArgs.Empty);
    }

    public void SetCategoryHandler(string categoryId, NexusDefaultAppId appId)
    {
        _config.CategoryHandlers[categoryId] = ToStorageValue(appId);
        SaveInternal(_config);
        PreferencesChanged?.Invoke(this, EventArgs.Empty);
    }

    private NexusDefaultAppId GetCategoryHandler(string categoryId, NexusDefaultAppId fallback)
    {
        if (_config.CategoryHandlers.TryGetValue(categoryId, out var stored) &&
            TryParseAppId(stored, out var parsed))
        {
            return parsed;
        }

        return fallback;
    }

    private NexusDefaultAppsConfig LoadInternal()
    {
        if (!File.Exists(_configPath))
        {
            return CreateDefaultConfig();
        }

        try
        {
            var json = File.ReadAllText(_configPath);
            var config = JsonSerializer.Deserialize<NexusDefaultAppsConfig>(json, JsonOptions);
            return config ?? CreateDefaultConfig();
        }
        catch
        {
            return CreateDefaultConfig();
        }
    }

    private void SaveInternal(NexusDefaultAppsConfig config)
    {
        _config = config;
        var json = JsonSerializer.Serialize(config, JsonOptions);
        File.WriteAllText(_configPath, json);
    }

    private static NexusDefaultAppsConfig CreateDefaultConfig()
    {
        var config = new NexusDefaultAppsConfig();
        foreach (var category in NexusFileAssociations.GetCategories())
        {
            config.CategoryHandlers[category.Id] = ToStorageValue(category.DefaultApp);
        }

        return config;
    }

    private static bool TryParseAppId(string value, out NexusDefaultAppId appId)
    {
        if (Enum.TryParse(value, ignoreCase: true, out appId))
        {
            return true;
        }

        return value.ToLowerInvariant() switch
        {
            "photos" => Assign(NexusDefaultAppId.Photos, out appId),
            "media" => Assign(NexusDefaultAppId.Media, out appId),
            "files" or "files-preview" => Assign(NexusDefaultAppId.FilesPreview, out appId),
            "browser" => Assign(NexusDefaultAppId.Browser, out appId),
            _ => false,
        };
    }

    private static bool Assign(NexusDefaultAppId value, out NexusDefaultAppId appId)
    {
        appId = value;
        return true;
    }

    private static string ToStorageValue(NexusDefaultAppId appId) => appId switch
    {
        NexusDefaultAppId.Photos => "photos",
        NexusDefaultAppId.Media => "media",
        NexusDefaultAppId.FilesPreview => "files-preview",
        NexusDefaultAppId.Browser => "browser",
        _ => appId.ToString().ToLowerInvariant(),
    };

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        WriteIndented = true,
    };
}
