using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace PowderCoating.Web.TagHelpers;

/// <summary>
/// Razor tag helper that transforms any <c>&lt;th sortable="ColumnName"&gt;</c>
/// element into a clickable, sortable column header with a sort-direction chevron.
/// <para>
/// Usage in a Razor view:
/// <code>
/// &lt;th sortable="CustomerName"
///     current-sort="@Model.SortColumn"
///     current-direction="@Model.SortDirection"&gt;
///     Customer
/// &lt;/th&gt;
/// </code>
/// </para>
/// <para>
/// Design decisions:
/// <list type="bullet">
///   <item>
///     All existing query-string parameters (search term, page number, filters)
///     are preserved when building the sort URL.  This means clicking a column
///     header does not reset an active search — a common UX frustration in
///     simpler implementations.
///   </item>
///   <item>
///     The page number is explicitly reset to 1 when the sort column changes
///     because sorted data shifts row positions, making a previously-active
///     page number meaningless.
///   </item>
///   <item>
///     The tag helper targets <c>&lt;th&gt;</c> elements (rather than a custom
///     element) so that existing table markup does not need structural changes —
///     adding the <c>sortable</c> attribute is sufficient.
///   </item>
///   <item>
///     The <c>sortable</c> css class is merged with any existing class on the
///     <c>&lt;th&gt;</c> (e.g. <c>text-end</c>, <c>ps-4</c>) so column-specific
///     alignment and spacing are preserved.
///   </item>
/// </list>
/// </para>
/// </summary>
[HtmlTargetElement("th", Attributes = "sortable")]
public class SortableColumnTagHelper : TagHelper
{
    /// <summary>
    /// The column identifier that will be sent as the <c>sortColumn</c> query
    /// parameter when the header link is clicked.  This should match the string
    /// the controller uses to switch on sort behaviour.
    /// </summary>
    [HtmlAttributeName("sortable")]
    public string ColumnName { get; set; } = string.Empty;

    /// <summary>
    /// The column currently being sorted, passed from the view model.
    /// When this equals <see cref="ColumnName"/> the header shows a directional
    /// chevron; otherwise a neutral double-arrow icon is shown.
    /// </summary>
    [HtmlAttributeName("current-sort")]
    public string? CurrentSort { get; set; }

    /// <summary>
    /// The current sort direction (<c>"asc"</c> or <c>"desc"</c>), passed from
    /// the view model.  Clicking the header toggles this to the opposite value.
    /// </summary>
    [HtmlAttributeName("current-direction")]
    public string? CurrentDirection { get; set; }

    /// <summary>
    /// The current view context, injected automatically by the Razor tag helper
    /// infrastructure.  <see cref="HtmlAttributeNotBound"/> prevents Razor from
    /// treating <c>view-context</c> as an HTML attribute on the element.
    /// Required to access <c>HttpContext.Request</c> for reading existing
    /// query string parameters.
    /// </summary>
    [ViewContext]
    [HtmlAttributeNotBound]
    public ViewContext ViewContext { get; set; } = null!;

    /// <summary>
    /// Rewrites the <c>&lt;th&gt;</c> element's content to contain a sort link.
    /// The method:
    /// <list type="number">
    ///   <item>Copies all existing query parameters from the current URL.</item>
    ///   <item>Overrides <c>sortColumn</c>, <c>sortDirection</c>, and <c>pageNumber</c>.</item>
    ///   <item>Selects the appropriate Bootstrap Icon chevron for the current sort state.</item>
    ///   <item>Wraps the cell's original text in an anchor tag pointing to the new URL.</item>
    /// </list>
    /// </summary>
    /// <param name="context">Tag helper execution context (provides access to all attributes).</param>
    /// <param name="output">The output object that controls the rendered HTML.</param>
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var request = ViewContext.HttpContext.Request;
        var queryParams = request.Query.ToDictionary(k => k.Key, v => v.Value.ToString());

        // Determine new sort direction
        var newDirection = "asc";
        if (CurrentSort == ColumnName && CurrentDirection == "asc")
        {
            newDirection = "desc";
        }

        // Update query params
        queryParams["sortColumn"] = ColumnName;
        queryParams["sortDirection"] = newDirection;
        queryParams["pageNumber"] = "1"; // Reset to page 1 when sorting changes

        // Build query string
        var queryString = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value)}"));
        var url = $"{request.Path}?{queryString}";

        // Determine icon
        var icon = "bi-arrow-down-up";
        if (CurrentSort == ColumnName)
        {
            icon = CurrentDirection == "asc" ? "bi-arrow-up" : "bi-arrow-down";
        }

        // Build output — preserve any existing classes (e.g. text-end, ps-4)
        var existingClass = output.Attributes["class"]?.Value?.ToString() ?? "";
        var combinedClass = string.IsNullOrEmpty(existingClass) ? "sortable" : $"sortable {existingClass}";
        output.Attributes.SetAttribute("class", combinedClass);

        // Use the tag's inner content as link text so display text matches the markup
        var innerContent = (await output.GetChildContentAsync()).GetContent();
        output.Content.Clear();
        output.Content.AppendHtml($"<a href=\"{url}\" class=\"text-decoration-none\" style=\"color:inherit\">");
        output.Content.AppendHtml(string.IsNullOrWhiteSpace(innerContent) ? context.AllAttributes["sortable"].Value.ToString() : innerContent);
        output.Content.AppendHtml($" <i class=\"bi {icon}\"></i>");
        output.Content.AppendHtml("</a>");
    }
}
