using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Csv;

namespace ZB.MOM.WW.OtOpcUa.Commons.Tests.Csv;

/// <summary>
///     Quote-on-demand and terminator behaviour for <see cref="CsvWriter"/>: a field is quoted only when
///     it must be (delimiter / quote / CR / LF), internal quotes double, CRLF terminates records by
///     default (configurable), and no terminator trails the final row.
/// </summary>
public sealed class CsvWriterTests
{
    [Fact]
    public void Writes_simple_rows_with_crlf_default()
    {
        var csv = CsvWriter.WriteToString(new[]
        {
            new[] { "a", "b", "c" },
            new[] { "d", "e", "f" },
        });

        csv.ShouldBe("a,b,c\r\nd,e,f");
    }

    [Fact]
    public void No_trailing_newline_after_last_row()
    {
        CsvWriter.WriteToString(new[] { new[] { "a" } }).ShouldBe("a");
    }

    [Fact]
    public void Empty_rows_yield_empty_string()
    {
        CsvWriter.WriteToString(Array.Empty<string[]>()).ShouldBe("");
    }

    // ---- quote-on-demand ----

    [Fact]
    public void Plain_field_is_not_quoted()
    {
        CsvWriter.WriteToString(new[] { new[] { "plain" } }).ShouldBe("plain");
    }

    [Fact]
    public void Field_with_comma_is_quoted()
    {
        CsvWriter.WriteToString(new[] { new[] { "a,b" } }).ShouldBe("\"a,b\"");
    }

    [Fact]
    public void Field_with_quote_is_quoted_and_doubled()
    {
        CsvWriter.WriteToString(new[] { new[] { "a\"b" } }).ShouldBe("\"a\"\"b\"");
    }

    [Fact]
    public void Field_with_lf_is_quoted()
    {
        CsvWriter.WriteToString(new[] { new[] { "a\nb" } }).ShouldBe("\"a\nb\"");
    }

    [Fact]
    public void Field_with_cr_is_quoted()
    {
        CsvWriter.WriteToString(new[] { new[] { "a\rb" } }).ShouldBe("\"a\rb\"");
    }

    [Fact]
    public void Leading_and_trailing_spaces_are_not_quoted()
    {
        // RFC does not require quoting for spaces; the parser preserves them either way.
        CsvWriter.WriteToString(new[] { new[] { " a " } }).ShouldBe(" a ");
    }

    [Fact]
    public void Empty_fields_are_written_bare()
    {
        CsvWriter.WriteToString(new[] { new[] { "a", "", "c" } }).ShouldBe("a,,c");
    }

    [Fact]
    public void Null_field_is_written_as_empty()
    {
        CsvWriter.WriteToString(new[] { new[] { "a", null, "c" } }).ShouldBe("a,,c");
    }

    // ---- options ----

    [Fact]
    public void QuoteAllFields_forces_every_field_quoted()
    {
        CsvWriter.WriteToString(new[] { new[] { "a", "b" } }, quoteAllFields: true)
            .ShouldBe("\"a\",\"b\"");
    }

    [Fact]
    public void Custom_newline_lf()
    {
        CsvWriter.WriteToString(new[] { new[] { "a" }, new[] { "b" } }, newline: "\n")
            .ShouldBe("a\nb");
    }

    [Fact]
    public void Custom_delimiter_semicolon_quotes_on_semicolon_not_comma()
    {
        CsvWriter.WriteToString(new[] { new[] { "a,b", "c;d" } }, delimiter: ';')
            .ShouldBe("a,b;\"c;d\"");
    }

    [Fact]
    public void Writes_to_a_textwriter()
    {
        var sw = new StringWriter();
        CsvWriter.Write(sw, new[] { new[] { "x", "y" } });
        sw.ToString().ShouldBe("x,y");
    }

    [Fact]
    public void Delimiter_may_not_be_a_quote()
    {
        Should.Throw<ArgumentException>(() =>
            CsvWriter.WriteToString(new[] { new[] { "a" } }, delimiter: '"'));
    }
}
