using System.CommandLine;
using ZB.MOM.WW.ScadaBridge.CLI.Commands;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;

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

/// <summary>
/// MV-11: the <c>template attribute add</c> / <c>update</c> commands must support
/// structured multi-value (List) attributes — a new <c>--element-type</c> option,
/// a JSON-array <c>--value</c>, client-side element-type validation, and
/// <see cref="AddTemplateAttributeCommand.ElementDataType"/> /
/// <see cref="UpdateTemplateAttributeCommand.ElementDataType"/> wired into the
/// payload sent to the Management API.
/// </summary>
public class TemplateAttributeListTests
{
    private static readonly Option<string> Url = new("--url") { Recursive = true };
    private static readonly Option<string> Username = new("--username") { Recursive = true };
    private static readonly Option<string> Password = new("--password") { Recursive = true };
    private static readonly Option<string> Format = CliOptions.CreateFormatOption();

    private static Command AttributeGroup()
        => TemplateCommands.Build(Url, Format, Username, Password)
            .Subcommands.Single(c => c.Name == "attribute");

    // ---- option surface ----

    [Fact]
    public void AttributeAdd_HasElementTypeOption()
    {
        var add = AttributeGroup().Subcommands.Single(c => c.Name == "add");
        Assert.Contains("--element-type", add.Options.Select(o => o.Name));
    }

    [Fact]
    public void AttributeUpdate_HasElementTypeOption()
    {
        var update = AttributeGroup().Subcommands.Single(c => c.Name == "update");
        Assert.Contains("--element-type", update.Options.Select(o => o.Name));
    }

    // ---- client-side element-type validation (both directions) ----

    [Theory]
    [InlineData("String")]
    [InlineData("Int32")]
    [InlineData("Float")]
    [InlineData("Double")]
    [InlineData("Boolean")]
    [InlineData("DateTime")]
    public void ValidateElementType_ListWithValidScalar_Ok(string elementType)
    {
        var ok = TemplateCommands.TryValidateElementType("List", elementType, out var error);
        Assert.True(ok);
        Assert.Null(error);
    }

    [Fact]
    public void ValidateElementType_ListWithValidScalar_CaseInsensitive()
    {
        var ok = TemplateCommands.TryValidateElementType("List", "string", out var error);
        Assert.True(ok);
        Assert.Null(error);
    }

    [Fact]
    public void ValidateElementType_ListWithoutElementType_Error()
    {
        var ok = TemplateCommands.TryValidateElementType("List", null, out var error);
        Assert.False(ok);
        Assert.NotNull(error);
    }

    [Fact]
    public void ValidateElementType_ListWithBlankElementType_Error()
    {
        var ok = TemplateCommands.TryValidateElementType("List", "   ", out var error);
        Assert.False(ok);
        Assert.NotNull(error);
    }

    [Fact]
    public void ValidateElementType_ListWithInvalidScalar_Error()
    {
        var ok = TemplateCommands.TryValidateElementType("List", "List", out var error);
        Assert.False(ok);
        Assert.NotNull(error);
    }

    [Fact]
    public void ValidateElementType_ListWithBinaryScalar_Error()
    {
        // Binary is a DataType but not a permitted List element scalar.
        var ok = TemplateCommands.TryValidateElementType("List", "Binary", out var error);
        Assert.False(ok);
        Assert.NotNull(error);
    }

    [Fact]
    public void ValidateElementType_ScalarWithElementType_Error()
    {
        var ok = TemplateCommands.TryValidateElementType("String", "Int32", out var error);
        Assert.False(ok);
        Assert.NotNull(error);
    }

    [Fact]
    public void ValidateElementType_ScalarWithoutElementType_Ok()
    {
        var ok = TemplateCommands.TryValidateElementType("Float", null, out var error);
        Assert.True(ok);
        Assert.Null(error);
    }

    // ---- payload wiring: the raw JSON value + ElementDataType flow into the command ----

    [Fact]
    public void BuildAddCommand_ListAttribute_CarriesElementTypeAndRawJsonValue()
    {
        var cmd = TemplateCommands.BuildAddAttributeCommand(
            templateId: 7,
            name: "WorkOrders",
            dataType: "List",
            value: """["WO-1","WO-2"]""",
            description: null,
            dataSource: null,
            isLocked: false,
            elementType: "String");

        Assert.Equal(7, cmd.TemplateId);
        Assert.Equal("List", cmd.DataType);
        Assert.Equal("String", cmd.ElementDataType);
        // The CLI forwards the raw JSON string unchanged — the API/codec parses it.
        Assert.Equal("""["WO-1","WO-2"]""", cmd.Value);
    }

    [Fact]
    public void BuildUpdateCommand_ListAttribute_CarriesElementTypeAndRawJsonValue()
    {
        var cmd = TemplateCommands.BuildUpdateAttributeCommand(
            attributeId: 42,
            name: "WorkOrders",
            dataType: "List",
            value: """["A","B"]""",
            description: null,
            dataSource: null,
            isLocked: false,
            elementType: "Int32");

        Assert.Equal(42, cmd.AttributeId);
        Assert.Equal("List", cmd.DataType);
        Assert.Equal("Int32", cmd.ElementDataType);
        Assert.Equal("""["A","B"]""", cmd.Value);
    }

    [Fact]
    public void BuildAddCommand_ScalarAttribute_LeavesElementTypeNull()
    {
        var cmd = TemplateCommands.BuildAddAttributeCommand(
            templateId: 1,
            name: "Speed",
            dataType: "Float",
            value: "0",
            description: null,
            dataSource: null,
            isLocked: false,
            elementType: null);

        Assert.Null(cmd.ElementDataType);
        Assert.Equal("Float", cmd.DataType);
    }
}
