using Bunit;
using Bunit.TestDoubles;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Health;
using ZB.MOM.WW.ScadaBridge.Commons.Types;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Health;

/// <summary>
/// bUnit tests for <see cref="AuditKpiTiles"/> (#23 M7 Bundle E / M7-T13). The
/// component renders three Bootstrap-card tiles — Volume, Error Rate, Backlog —
/// from a single <see cref="AuditLogKpiSnapshot"/>. The tests pin:
///
/// <list type="bullet">
///   <item>Three-tile render contract (data-test attributes for stable selectors).</item>
///   <item>Error-rate maths: <c>ErrorEventsLastHour / TotalEventsLastHour</c> with
///   safe zero-events handling (no DivideByZero, displays "0.0%").</item>
///   <item>Unavailable snapshot renders em dashes plus the error message.</item>
///   <item>Tile clicks navigate to the correct pre-filtered Audit Log URL.</item>
/// </list>
/// </summary>
public class AuditKpiTilesTests : BunitContext
{
    private static AuditLogKpiSnapshot MakeSnapshot(long total, long errors, long backlog) =>
        new(total, errors, backlog, new DateTime(2026, 5, 20, 10, 0, 0, DateTimeKind.Utc));

    [Fact]
    public void Renders_ThreeTiles_FromSnapshot()
    {
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 120, errors: 3, backlog: 7))
            .Add(c => c.IsAvailable, true));

        // Three stable data-test selectors — these are the contract for both
        // tests and any future Playwright sweep.
        Assert.Contains("data-test=\"audit-kpi-volume\"", cut.Markup);
        Assert.Contains("data-test=\"audit-kpi-error-rate\"", cut.Markup);
        Assert.Contains("data-test=\"audit-kpi-backlog\"", cut.Markup);

        // Tile values render the snapshot's counters.
        Assert.Contains("120", cut.Markup);  // volume
        Assert.Contains("7", cut.Markup);    // backlog
    }

    [Fact]
    public void ErrorRate_Computed_From_Total_AndErrors()
    {
        // 5 errors out of 100 → 5.0%.
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 100, errors: 5, backlog: 0))
            .Add(c => c.IsAvailable, true));

        Assert.Contains("5.0%", cut.Markup);
    }

    [Fact]
    public void ZeroEvents_DoesNotDivideByZero_RendersZeroPercent()
    {
        // Total = 0 → naïve division would throw or yield NaN. The tile must
        // render "0.0%" instead (zero events means zero errors too — a real
        // signal, not an unavailability marker).
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 0, errors: 0, backlog: 0))
            .Add(c => c.IsAvailable, true));

        Assert.Contains("0.0%", cut.Markup);
        // And the volume tile shows "0", not an em dash — the snapshot itself
        // is available; the system was just quiet for the hour.
        Assert.Contains("data-test=\"audit-kpi-volume\"", cut.Markup);
    }

    [Fact]
    public void UnavailableSnapshot_RendersEmDashes_AndErrorMessage()
    {
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, (AuditLogKpiSnapshot?)null)
            .Add(c => c.IsAvailable, false)
            .Add(c => c.ErrorMessage, "DB connection refused"));

        // All three tiles show em dashes — em dash (U+2014) "—" must appear.
        Assert.Contains("—", cut.Markup);
        // Inline error message renders below.
        Assert.Contains("Audit KPIs unavailable", cut.Markup);
        Assert.Contains("DB connection refused", cut.Markup);
    }

    [Fact]
    public void ErrorRateTile_Click_NavigatesToAuditLog_WithFailedStatusFilter()
    {
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 50, errors: 3, backlog: 0))
            .Add(c => c.IsAvailable, true));

        // bUnit's BunitNavigationManager records the last URI a Navigation.NavigateTo call hit.
        var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();

        var tile = cut.Find("[data-test=\"audit-kpi-error-rate\"]");
        tile.Click();

        // Spec: error-rate tile drills into ?status=Failed.
        Assert.Contains("/audit/log?status=Failed", nav.Uri);
    }

    [Fact]
    public void VolumeTile_Click_NavigatesToUnfilteredAuditLog()
    {
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 50, errors: 3, backlog: 0))
            .Add(c => c.IsAvailable, true));

        var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
        var tile = cut.Find("[data-test=\"audit-kpi-volume\"]");
        tile.Click();

        // Unfiltered /audit/log — no query string.
        Assert.EndsWith("/audit/log", nav.Uri);
    }

    [Fact]
    public void BacklogTile_Click_NavigatesToAuditLog()
    {
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 50, errors: 0, backlog: 12))
            .Add(c => c.IsAvailable, true));

        var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
        var tile = cut.Find("[data-test=\"audit-kpi-backlog\"]");
        tile.Click();

        Assert.EndsWith("/audit/log", nav.Uri);
    }

    [Fact]
    public void NonzeroErrorRate_GetsWarningBorder_NotDangerBelowTenPercent()
    {
        // 5% is < 10% → warning border, not danger.
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 100, errors: 5, backlog: 0))
            .Add(c => c.IsAvailable, true));

        var tile = cut.Find("[data-test=\"audit-kpi-error-rate\"]");
        Assert.Contains("border-warning", tile.GetAttribute("class") ?? string.Empty);
        Assert.DoesNotContain("border-danger", tile.GetAttribute("class") ?? string.Empty);
    }

    [Fact]
    public void HighErrorRate_GetsDangerBorder()
    {
        // 25% is > 10% → danger border.
        var cut = Render<AuditKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(total: 100, errors: 25, backlog: 0))
            .Add(c => c.IsAvailable, true));

        var tile = cut.Find("[data-test=\"audit-kpi-error-rate\"]");
        Assert.Contains("border-danger", tile.GetAttribute("class") ?? string.Empty);
    }
}
