using System.Text.Json;

namespace RolemasterDb.App.Frontend.AppState;

public sealed class PinnedTablesState(BrowserStorageService browserStorage)
{
    private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
    private bool isInitialized;

    public IReadOnlyList<PinnedTableEntry> Items { get; private set; } = [];

    public event Action? Changed;

    public async Task InitializeAsync()
    {
        if (isInitialized)
        {
            return;
        }

        try
        {
            var storedValue = await browserStorage.GetItemAsync(BrowserStorageKeys.PinnedTables);
            Items = DeserializeItems(storedValue);
            isInitialized = true;
            Changed?.Invoke();
        }
        catch (JsonException)
        {
            Items = [];
            isInitialized = true;
            Changed?.Invoke();
        }
        catch (InvalidOperationException)
        {
            // JS interop is unavailable during prerender. Retry on the next interactive render.
        }
    }

    public bool IsPinned(string slug) =>
        Items.Any(item => string.Equals(item.Slug, slug, StringComparison.OrdinalIgnoreCase));

    public async Task ToggleAsync(string slug, string label, string family, int curationPercentage)
    {
        await InitializeAsync();

        if (IsPinned(slug))
        {
            await UnpinAsync(slug);
            return;
        }

        await PinAsync(slug, label, family, curationPercentage);
    }

    private async Task PinAsync(string slug, string label, string family, int curationPercentage)
    {
        if (string.IsNullOrWhiteSpace(slug) || string.IsNullOrWhiteSpace(label))
        {
            return;
        }

        var updatedItems = new List<PinnedTableEntry>
        {
            new(
                slug.Trim(),
                label.Trim(),
                family.Trim(),
                curationPercentage,
                DateTimeOffset.UtcNow)
        };

        foreach (var item in Items)
        {
            if (string.Equals(item.Slug, slug, StringComparison.OrdinalIgnoreCase))
            {
                continue;
            }

            updatedItems.Add(item);
        }

        Items = updatedItems;
        await PersistAsync();
        Changed?.Invoke();
    }

    private async Task UnpinAsync(string slug)
    {
        Items = Items
            .Where(item => !string.Equals(item.Slug, slug, StringComparison.OrdinalIgnoreCase))
            .ToList();

        await PersistAsync();
        Changed?.Invoke();
    }

    private async Task PersistAsync()
    {
        var serialized = JsonSerializer.Serialize(Items, SerializerOptions);
        await browserStorage.SetItemAsync(BrowserStorageKeys.PinnedTables, serialized);
    }

    private static IReadOnlyList<PinnedTableEntry> DeserializeItems(string? storedValue)
    {
        if (string.IsNullOrWhiteSpace(storedValue))
        {
            return [];
        }

        var items = JsonSerializer.Deserialize<List<PinnedTableEntry>>(storedValue, SerializerOptions);
        if (items is null || items.Count == 0)
        {
            return [];
        }

        return items
            .Where(item => !string.IsNullOrWhiteSpace(item.Slug) && !string.IsNullOrWhiteSpace(item.Label))
            .GroupBy(item => item.Slug, StringComparer.OrdinalIgnoreCase)
            .Select(group => group
                .OrderByDescending(item => item.PinnedAtUtc)
                .First())
            .OrderByDescending(item => item.PinnedAtUtc)
            .ToList();
    }
}
