using System.Text.Json;
using NucleicBroker;

namespace NucleicBroker.Tests;

public sealed class OutboundWriterTests
{
    [Fact]
    public async Task PreservesEnqueueOrderAcrossKinds()
    {
        var captured = new StringWriter();
        var writer = new OutboundWriter(captured);
        writer.EnqueueJson("""{"a":1}""");
        writer.EnqueueProcOutput(1, stderr: false, "one"u8);
        writer.EnqueueProcOutput(1, stderr: false, "two"u8);
        writer.EnqueueJson("""{"b":2}""");
        await writer.CompleteAsync();

        var lines = captured.ToString().Split('\n', StringSplitOptions.RemoveEmptyEntries);
        Assert.Equal("""{"a":1}""", lines[0]);
        Assert.Equal("""{"b":2}""", lines[^1]);

        // The two same-stream chunks arrive between the json lines, concatenated in order —
        // as ONE notification when the drain saw both queued, as two otherwise. Either way
        // the decoded byte stream is identical.
        var decoded = string.Concat(lines[1..^1].Select(l =>
        {
            var note = JsonDocument.Parse(l).RootElement;
            Assert.Equal("proc.stdout", note.GetProperty("method").GetString());
            return System.Text.Encoding.UTF8.GetString(
                Convert.FromBase64String(note.GetProperty("params").GetProperty("b64").GetString()!));
        }));
        Assert.Equal("onetwo", decoded);
    }

    [Fact]
    public async Task FramesLinesWithBareLineFeed()
    {
        // The wire is LF-framed on every host (docs/WINDOWS_PORT.md §3.3). TextWriter.NewLine
        // would otherwise be CRLF on Windows, hiding a stray \r inside every line hostd parses.
        // Forcing CRLF on the sink reproduces the Windows default on any platform, so this
        // pins the framing even when CI runs the suite on Linux.
        var captured = new StringWriter { NewLine = "\r\n" };
        var writer = new OutboundWriter(captured);
        writer.EnqueueJson("""{"a":1}""");
        writer.EnqueueProcOutput(1, stderr: false, "one"u8);
        await writer.CompleteAsync();

        var text = captured.ToString();
        Assert.DoesNotContain('\r', text);
        Assert.EndsWith("\n", text);
    }

    [Fact]
    public async Task StderrAndStdoutChunksNeverMerge()
    {
        var captured = new StringWriter();
        var writer = new OutboundWriter(captured);
        writer.EnqueueProcOutput(1, stderr: false, "out"u8);
        writer.EnqueueProcOutput(1, stderr: true, "err"u8);
        await writer.CompleteAsync();

        var methods = captured.ToString().Split('\n', StringSplitOptions.RemoveEmptyEntries)
            .Select(l => JsonDocument.Parse(l).RootElement.GetProperty("method").GetString())
            .ToList();
        Assert.Equal(["proc.stdout", "proc.stderr"], methods);
    }
}
