using Microsoft.AspNetCore.Components;
using Bunit;
using SufiChain.SufiBlazor.Components.Data;
using Xunit;

namespace SufiChain.SufiBlazor.Tests.Components.Data;

public class SbTableTests : BunitContext
{
    private static List<TableTestItem> CreateTestItems(int count)
    {
        return Enumerable.Range(1, count)
            .Select(i => new TableTestItem { Id = i, Name = $"Item {i}", Value = i * 10 })
            .ToList();
    }

    private static RenderFragment<TableTestItem> RowTemplate => item => builder =>
    {
        builder.OpenElement(0, "td");
        builder.AddContent(1, item.Name);
        builder.CloseElement();
        builder.OpenElement(2, "td");
        builder.AddContent(3, item.Value.ToString());
        builder.CloseElement();
    };

    private IRenderedComponent<SbTable<TableTestItem>> RenderTable(
        List<TableTestItem>? items = null,
        Action<ComponentParameterCollectionBuilder<SbTable<TableTestItem>>>? configure = null)
    {
        items ??= CreateTestItems(3);
        return Render<SbTable<TableTestItem>>(p =>
        {
            p.Add(x => x.Items, items)
                .Add(x => x.RowTemplate, RowTemplate);
            configure?.Invoke(p);
        });
    }

    [Fact]
    public void RendersTableStructure()
    {
        // Arrange & Act
        var cut = RenderTable();

        // Assert
        var container = cut.Find(".sb-table-container");
        Assert.NotNull(container);
        var table = cut.Find("table.sb-table");
        Assert.NotNull(table);
        Assert.NotNull(cut.Find("tbody.sb-table__body"));
    }

    [Fact]
    public void RendersDataRows()
    {
        // Arrange
        var items = CreateTestItems(2);

        // Act
        var cut = RenderTable(items);

        // Assert
        Assert.Contains("Item 1", cut.Markup);
        Assert.Contains("Item 2", cut.Markup);
        Assert.Contains("10", cut.Markup);
        Assert.Contains("20", cut.Markup);
        var rows = cut.FindAll(".sb-table__row");
        Assert.Equal(2, rows.Count);
    }

    [Fact]
    public void RendersHeaderWhenHeaderContentProvided()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p
            .Add(x => x.HeaderContent, h => h.AddMarkupContent(0, "<tr><th>Name</th><th>Value</th></tr>")));

        // Assert
        var thead = cut.Find("thead.sb-table__head");
        Assert.NotNull(thead);
        Assert.Contains("Name", cut.Markup);
        Assert.Contains("Value", cut.Markup);
    }

    [Fact]
    public void DoesNotRenderHeaderWhenHeaderContentNull()
    {
        // Arrange & Act
        var cut = RenderTable();

        // Assert
        Assert.Empty(cut.FindAll("thead"));
    }

    [Fact]
    public void RendersFooterWhenFooterContentProvided()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p
            .Add(x => x.FooterContent, f => f.AddMarkupContent(0, "<tr><td colspan=\"2\">Total: 3 items</td></tr>")));

        // Assert
        var tfoot = cut.Find("tfoot.sb-table__foot");
        Assert.NotNull(tfoot);
        Assert.Contains("Total: 3 items", cut.Markup);
    }

    [Fact]
    public void DoesNotRenderFooterWhenFooterContentNull()
    {
        // Arrange & Act
        var cut = RenderTable();

        // Assert
        Assert.Empty(cut.FindAll("tfoot"));
    }

    [Fact]
    public void RendersEmptyContentWhenNoItems()
    {
        // Arrange & Act
        var cut = Render<SbTable<TableTestItem>>(p => p
            .Add(x => x.Items, new List<TableTestItem>())
            .Add(x => x.RowTemplate, RowTemplate)
            .Add(x => x.EmptyContent, e => e.AddMarkupContent(0, "<span class=\"custom-empty\">No data</span>")));

        // Assert
        var emptyCell = cut.Find(".sb-table__empty");
        Assert.NotNull(emptyCell);
        var customEmpty = cut.Find(".custom-empty");
        Assert.NotNull(customEmpty);
        Assert.Contains("No data", customEmpty.TextContent);
    }

    [Fact]
    public void RendersEmptyContentWhenItemsNull()
    {
        // Arrange & Act
        var cut = Render<SbTable<TableTestItem>>(p => p
            .Add(x => x.Items, (IEnumerable<TableTestItem>?)null)
            .Add(x => x.RowTemplate, RowTemplate)
            .Add(x => x.EmptyContent, e => e.AddMarkupContent(0, "Nothing here")));

        // Assert
        Assert.Contains("Nothing here", cut.Markup);
    }

    [Fact]
    public void RendersEmptyBodyWhenNoItemsAndNoEmptyContent()
    {
        // Arrange & Act
        var cut = Render<SbTable<TableTestItem>>(p => p
            .Add(x => x.Items, new List<TableTestItem>())
            .Add(x => x.RowTemplate, RowTemplate));

        // Assert - tbody exists but has no data rows
        var tbody = cut.Find("tbody.sb-table__body");
        Assert.NotNull(tbody);
        Assert.Empty(cut.FindAll(".sb-table__row"));
    }

    [Fact]
    public void AppliesStripedClass()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p.Add(x => x.Striped, true));

        // Assert
        var table = cut.Find("table.sb-table");
        Assert.Contains("sb-table--striped", table.ClassList);
    }

    [Fact]
    public void AppliesHoverClass()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p.Add(x => x.Hover, true));

        // Assert - Hover defaults to true
        var table = cut.Find("table.sb-table");
        Assert.Contains("sb-table--hover", table.ClassList);
    }

    [Fact]
    public void DoesNotApplyHoverClassWhenHoverFalse()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p.Add(x => x.Hover, false));

        // Assert
        var table = cut.Find("table.sb-table");
        Assert.DoesNotContain("sb-table--hover", table.ClassList);
    }

    [Fact]
    public void AppliesBorderedClass()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p.Add(x => x.Bordered, true));

        // Assert
        var table = cut.Find("table.sb-table");
        Assert.Contains("sb-table--bordered", table.ClassList);
    }

    [Fact]
    public void AppliesCompactClass()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p.Add(x => x.Compact, true));

        // Assert
        var table = cut.Find("table.sb-table");
        Assert.Contains("sb-table--compact", table.ClassList);
    }

    [Fact]
    public void AppliesCustomClassToContainer()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p.Add(x => x.Class, "my-table"));

        // Assert
        var container = cut.Find(".sb-table-container");
        Assert.Contains("my-table", container.ClassList);
    }

    [Fact]
    public async Task InvokesOnRowClickWhenRowClicked()
    {
        // Arrange
        TableTestItem? capturedItem = null;
        var items = CreateTestItems(3);

        var cut = Render<SbTable<TableTestItem>>(p => p
            .Add(x => x.Items, items)
            .Add(x => x.RowTemplate, RowTemplate)
            .Add(x => x.OnRowClick, EventCallback.Factory.Create<TableTestItem>(this, item => capturedItem = item)));

        // Act - click second row (Item 2)
        var rows = cut.FindAll(".sb-table__row");
        var row2 = rows.FirstOrDefault(r => r.TextContent.Contains("Item 2"));
        Assert.NotNull(row2);
        await cut.InvokeAsync(() => row2!.Click());

        cut.WaitForState(() => capturedItem != null);

        // Assert
        Assert.NotNull(capturedItem);
        Assert.Equal(2, capturedItem!.Id);
        Assert.Equal("Item 2", capturedItem.Name);
        Assert.Equal(20, capturedItem.Value);
    }

    [Fact]
    public void RendersAllStyleVariantsTogether()
    {
        // Arrange & Act
        var cut = RenderTable(configure: p => p
            .Add(x => x.Striped, true)
            .Add(x => x.Hover, true)
            .Add(x => x.Bordered, true)
            .Add(x => x.Compact, true));

        // Assert
        var table = cut.Find("table.sb-table");
        Assert.Contains("sb-table--striped", table.ClassList);
        Assert.Contains("sb-table--hover", table.ClassList);
        Assert.Contains("sb-table--bordered", table.ClassList);
        Assert.Contains("sb-table--compact", table.ClassList);
    }

    private class TableTestItem
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public int Value { get; set; }
    }
}
