using System.Text.Json;
using ZB.MOM.WW.ScadaBridge.CLI.Commands;

namespace ZB.MOM.WW.ScadaBridge.CLI.Tests.Commands;

/// <summary>
/// Tests for the <c>table</c> output formatter of the <c>scadabridge audit query</c>
/// subcommand (Audit Log #23 M8-T6): header rendering, long-field truncation, the
/// empty-result-set case, and null-actor handling.
/// </summary>
public class AuditTableFormatterTests
{
    private static IReadOnlyList<JsonElement> Events(string json)
    {
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.EnumerateArray()
            .Select(e => e.Clone())
            .ToList();
    }

    [Fact]
    public void Table_RendersHeaderRow_WithExpectedColumns()
    {
        var formatter = new TableAuditFormatter();
        var output = new StringWriter();

        formatter.WritePage(Events("[]"), output);

        var firstLine = output.ToString()
            .Split('\n', StringSplitOptions.RemoveEmptyEntries)[0];
        foreach (var col in new[]
                 {
                     "OccurredAtUtc", "Channel", "Kind", "Status",
                     "Target", "Actor", "DurationMs", "HttpStatus",
                 })
        {
            Assert.Contains(col, firstLine);
        }
    }

    [Fact]
    public void Table_TruncatesLongTarget_WithEllipsis()
    {
        var formatter = new TableAuditFormatter();
        var output = new StringWriter();

        var longTarget = new string('x', 200);
        formatter.WritePage(
            Events($"[{{\"occurredAtUtc\":\"2026-05-20T12:00:00Z\",\"channel\":\"OutboundApi\"," +
                   $"\"kind\":\"SyncCall\",\"status\":\"Delivered\",\"target\":\"{longTarget}\"," +
                   $"\"actor\":\"multi-role\"}}]"),
            output);

        var text = output.ToString();
        Assert.Contains("…", text);
        // The full untruncated target must not appear verbatim.
        Assert.DoesNotContain(longTarget, text);
    }

    [Fact]
    public void Table_EmptyResultSet_RendersHeaderOnly_OrNoRowsMessage()
    {
        var formatter = new TableAuditFormatter();
        var output = new StringWriter();

        formatter.WritePage(Events("[]"), output);

        var lines = output.ToString()
            .Split('\n', StringSplitOptions.RemoveEmptyEntries);
        // Header only — no data rows. (A header line is always emitted so the
        // column shape is visible even with zero results.)
        Assert.Single(lines);
        Assert.Contains("OccurredAtUtc", lines[0]);
    }

    [Fact]
    public void Table_NullActor_RendersBlank()
    {
        var formatter = new TableAuditFormatter();
        var output = new StringWriter();

        formatter.WritePage(
            Events("[{\"occurredAtUtc\":\"2026-05-20T12:00:00Z\",\"channel\":\"InboundApi\"," +
                   "\"kind\":\"ApiCall\",\"status\":\"Delivered\",\"target\":\"key-1\"," +
                   "\"actor\":null}]"),
            output);

        var lines = output.ToString()
            .Split('\n', StringSplitOptions.RemoveEmptyEntries);
        Assert.Equal(2, lines.Length);
        // The data row must not contain the literal "null" for the actor column.
        Assert.DoesNotContain("null", lines[1]);
        Assert.Contains("InboundApi", lines[1]);
    }

    [Fact]
    public void Table_HeaderEmittedOncePerPage_DataRowsAligned()
    {
        var formatter = new TableAuditFormatter();
        var output = new StringWriter();

        formatter.WritePage(
            Events("[{\"occurredAtUtc\":\"2026-05-20T12:00:00Z\",\"channel\":\"OutboundApi\"," +
                   "\"kind\":\"SyncCall\",\"status\":\"Delivered\",\"target\":\"weather-api\"," +
                   "\"actor\":\"multi-role\",\"durationMs\":42,\"httpStatus\":200}," +
                   "{\"occurredAtUtc\":\"2026-05-20T12:01:00Z\",\"channel\":\"Notification\"," +
                   "\"kind\":\"Send\",\"status\":\"Failed\",\"target\":\"ops-list\"," +
                   "\"actor\":\"scheduler\",\"durationMs\":7}]"),
            output);

        var lines = output.ToString()
            .Split('\n', StringSplitOptions.RemoveEmptyEntries);
        Assert.Equal(3, lines.Length);
        Assert.Contains("weather-api", lines[1]);
        Assert.Contains("ops-list", lines[2]);
    }
}
