using JSMR.Application.Common.Search;
using JSMR.Application.Tags.Queries.Search;
using JSMR.Application.Tags.Queries.Search.Contracts;
using Microsoft.AspNetCore.WebUtilities;
using System.Globalization;

namespace JSMR.UI.Blazor.Filters;

public sealed record TagFilterState : IFilterState<SearchTagsRequest>
{
    public string? Keywords { get; init; }
    public int PageNumber { get; init; } = 1;
    public int PageSize { get; init; } = 100;

    public IReadOnlyList<SortOption<TagSortField>> SortOptions { get; init; } =
    [
        new(TagSortField.Name, SortDirection.Ascending)
    ];

    public QueryParameters ToQuery()
    {
        QueryParameters query = [];

        query.Set("keywords", Keywords);

        if (PageNumber != 1)
            query.Set("pageNumber", PageNumber.ToString(CultureInfo.InvariantCulture));

        if (PageSize != 100)
            query.Set("pageSize", PageSize.ToString(CultureInfo.InvariantCulture));

        if (!IsDefaultSort(SortOptions))
        {
            query.Set("sort", string.Join(",", SortOptions.Select(x =>
                $"{x.Field}:{ToQueryDirection(x.Direction)}")));
        }

        return query;
    }

    public static TagFilterState FromQuery(string query)
    {
        QueryParameters queryParameters = new(QueryHelpers.ParseQuery(query));

        return new TagFilterState
        {
            Keywords = queryParameters.GetValue("keywords"),
            PageNumber = Math.Max(1, queryParameters.GetInteger("pageNumber", 1)),
            PageSize = queryParameters.GetInteger("pageSize", 100),
            SortOptions = ParseSortOptions(queryParameters.GetValue("sort"))
        };
    }

    public SearchTagsRequest ToSearchRequest()
    {
        return new(
            Options: new()
            {
                PageNumber = PageNumber,
                PageSize = PageSize,
                Criteria = new()
                {
                    Name = Keywords
                },
                SortOptions = [.. SortOptions]
            }
        );
    }

    private static IReadOnlyList<SortOption<TagSortField>> ParseSortOptions(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
            return DefaultSort();

        var result = new List<SortOption<TagSortField>>();

        foreach (var part in value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
        {
            var pieces = part.Split(':', 2, StringSplitOptions.TrimEntries);

            if (pieces.Length != 2)
                continue;

            if (!Enum.TryParse<TagSortField>(pieces[0], ignoreCase: true, out var field))
                continue;

            var direction = pieces[1].Equals("desc", StringComparison.OrdinalIgnoreCase)
                ? Application.Common.Search.SortDirection.Descending
                : Application.Common.Search.SortDirection.Ascending;

            result.Add(new(field, direction));
        }

        return result.Count > 0 ? result : DefaultSort();
    }

    private static IReadOnlyList<SortOption<TagSortField>> DefaultSort() =>
    [
        new(TagSortField.Name, Application.Common.Search.SortDirection.Ascending)
    ];

    private static string ToQueryDirection(Application.Common.Search.SortDirection direction)
        => direction == Application.Common.Search.SortDirection.Descending ? "desc" : "asc";

    private static bool IsDefaultSort(IReadOnlyList<SortOption<TagSortField>> sortOptions)
        => sortOptions.Count == 1
           && sortOptions[0].Field == TagSortField.Name
           && sortOptions[0].Direction == Application.Common.Search.SortDirection.Ascending;
}