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.Messages.Audit;

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

/// <summary>
/// bUnit tests for <see cref="SiteCallKpiTiles"/> (Site Call Audit #22, Task 7).
/// The component renders three Bootstrap-card tiles — Buffered, Stuck, Parked —
/// from a single <see cref="SiteCallKpiResponse"/> snapshot. The tests pin:
///
/// <list type="bullet">
///   <item>Three-tile render contract (data-test attributes for stable selectors).</item>
///   <item>Tile values render the snapshot's counters.</item>
///   <item>Threshold borders fire correctly — danger on Parked &gt; 0, warning
///   on Stuck &gt; 0, none when those counts are zero, none on Buffered.</item>
///   <item>Unavailable snapshot renders em dashes plus the error message.</item>
///   <item>Tile clicks navigate to the correct pre-filtered Site Calls report URL.</item>
/// </list>
/// </summary>
public class SiteCallKpiTilesTests : BunitContext
{
    private static SiteCallKpiResponse MakeSnapshot(int buffered, int parked, int stuck) =>
        new(
            CorrelationId: "k",
            Success: true,
            ErrorMessage: null,
            BufferedCount: buffered,
            ParkedCount: parked,
            FailedLastInterval: 0,
            DeliveredLastInterval: 0,
            OldestPendingAge: null,
            StuckCount: stuck);

    [Fact]
    public void Renders_ThreeTiles_FromSnapshot()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 120, parked: 3, stuck: 7))
            .Add(c => c.IsAvailable, true));

        // Three stable data-test selectors — the contract for both these tests
        // and any future Playwright sweep.
        Assert.Contains("data-test=\"site-call-kpi-buffered\"", cut.Markup);
        Assert.Contains("data-test=\"site-call-kpi-stuck\"", cut.Markup);
        Assert.Contains("data-test=\"site-call-kpi-parked\"", cut.Markup);

        // Tile values render the snapshot's counters.
        Assert.Contains(">120<", cut.Markup);  // buffered
        Assert.Contains(">7<", cut.Markup);    // stuck
        Assert.Contains(">3<", cut.Markup);    // parked
    }

    [Fact]
    public void UnavailableSnapshot_RendersEmDashes_AndErrorMessage()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, (SiteCallKpiResponse?)null)
            .Add(c => c.IsAvailable, false)
            .Add(c => c.ErrorMessage, "site call repository unavailable"));

        // All three tiles show em dashes — em dash (U+2014) "—" must appear.
        Assert.Contains("—", cut.Markup);
        // Inline error message renders below.
        Assert.Contains("Site Call KPIs unavailable", cut.Markup);
        Assert.Contains("site call repository unavailable", cut.Markup);
    }

    [Fact]
    public void ParkedTile_GetsDangerBorder_WhenParkedAboveZero()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 0, parked: 4, stuck: 0))
            .Add(c => c.IsAvailable, true));

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

    [Fact]
    public void ParkedTile_NoDangerBorder_WhenParkedZero()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 9, parked: 0, stuck: 0))
            .Add(c => c.IsAvailable, true));

        var tile = cut.Find("[data-test=\"site-call-kpi-parked\"]");
        Assert.DoesNotContain("border-danger", tile.GetAttribute("class") ?? string.Empty);
    }

    [Fact]
    public void StuckTile_GetsWarningBorder_WhenStuckAboveZero()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 0, parked: 0, stuck: 6))
            .Add(c => c.IsAvailable, true));

        var tile = cut.Find("[data-test=\"site-call-kpi-stuck\"]");
        Assert.Contains("border-warning", tile.GetAttribute("class") ?? string.Empty);
        // Warning, not danger — Stuck is the softer signal.
        Assert.DoesNotContain("border-danger", tile.GetAttribute("class") ?? string.Empty);
    }

    [Fact]
    public void StuckTile_NoWarningBorder_WhenStuckZero()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 9, parked: 0, stuck: 0))
            .Add(c => c.IsAvailable, true));

        var tile = cut.Find("[data-test=\"site-call-kpi-stuck\"]");
        Assert.DoesNotContain("border-warning", tile.GetAttribute("class") ?? string.Empty);
    }

    [Fact]
    public void BufferedTile_HasNoThresholdBorder_EvenWithHighCount()
    {
        // A non-zero buffer is normal operation — the Buffered tile is a plain
        // count tile and never gets a danger/warning border.
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 5000, parked: 0, stuck: 0))
            .Add(c => c.IsAvailable, true));

        var tile = cut.Find("[data-test=\"site-call-kpi-buffered\"]");
        var cls = tile.GetAttribute("class") ?? string.Empty;
        Assert.DoesNotContain("border-danger", cls);
        Assert.DoesNotContain("border-warning", cls);
    }

    [Fact]
    public void BufferedTile_Click_NavigatesToUnfilteredSiteCallsReport()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 50, parked: 0, stuck: 0))
            .Add(c => c.IsAvailable, true));

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

        // Unfiltered /site-calls/report — no query string.
        Assert.EndsWith("/site-calls/report", nav.Uri);
    }

    [Fact]
    public void StuckTile_Click_NavigatesToSiteCallsReport_WithStuckFilter()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 0, parked: 0, stuck: 6))
            .Add(c => c.IsAvailable, true));

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

        // Spec: Stuck tile drills into the report's "stuck only" filter.
        Assert.Contains("/site-calls/report?stuck=true", nav.Uri);
    }

    [Fact]
    public void ParkedTile_Click_NavigatesToSiteCallsReport_WithParkedStatusFilter()
    {
        var cut = Render<SiteCallKpiTiles>(p => p
            .Add(c => c.Snapshot, MakeSnapshot(buffered: 0, parked: 4, stuck: 0))
            .Add(c => c.IsAvailable, true));

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

        // Spec: Parked tile drills into ?status=Parked.
        Assert.Contains("/site-calls/report?status=Parked", nav.Uri);
    }
}
