using Bunit;
using ScadaLink.Commons.Types.DataConnections;
using ScadaLink.Commons.Types.Flattening;
using ScadaLink.Commons.Validators;
using OpcUaEndpointEditor = ScadaLink.CentralUI.Components.Forms.OpcUaEndpointEditor;

namespace ScadaLink.CentralUI.Tests.Forms;

public class OpcUaEndpointEditorTests : BunitContext
{
    [Fact]
    public void Renders_All_Four_Section_Labels()
    {
        var config = new OpcUaEndpointConfig();
        var cut = Render<OpcUaEndpointEditor>(p => p
            .Add(c => c.Config, config)
            .Add(c => c.Title, "Primary Endpoint"));

        Assert.Contains("Primary Endpoint", cut.Markup);
        Assert.Contains("Timing", cut.Markup);
        Assert.Contains("Subscription", cut.Markup);
        Assert.Contains("Heartbeat", cut.Markup);
    }

    [Fact]
    public void Binding_MutatesPassedConfigInstance()
    {
        var config = new OpcUaEndpointConfig();
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        cut.Find("input[type='text']").Change("opc.tcp://new-host:4840");

        Assert.Equal("opc.tcp://new-host:4840", config.EndpointUrl);
    }

    [Fact]
    public void EnableHeartbeat_CreatesSubObject()
    {
        var config = new OpcUaEndpointConfig();
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        Assert.Null(config.Heartbeat);
        cut.FindAll("button").First(b => b.TextContent.Contains("Enable Heartbeat")).Click();

        Assert.NotNull(config.Heartbeat);
    }

    [Fact]
    public void RemoveHeartbeat_NullsSubObject()
    {
        var config = new OpcUaEndpointConfig
        {
            Heartbeat = new OpcUaHeartbeatConfig { TagPath = "Hb", MaxSilenceSeconds = 30 }
        };
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        cut.FindAll("button").First(b => b.TextContent.Contains("Remove Heartbeat")).Click();

        Assert.Null(config.Heartbeat);
    }

    [Fact]
    public void Errors_Parameter_RendersPerFieldRedText()
    {
        var config = new OpcUaEndpointConfig { EndpointUrl = "" };
        var errors = OpcUaEndpointConfigValidator.Validate(config, "Primary.");
        var cut = Render<OpcUaEndpointEditor>(p => p
            .Add(c => c.Config, config)
            .Add(c => c.Errors, errors));

        Assert.Contains("Endpoint URL is required.", cut.Markup);
        Assert.Contains("text-danger", cut.Markup);
    }

    [Fact]
    public void IsLegacy_True_RendersWarningBanner()
    {
        var cut = Render<OpcUaEndpointEditor>(p => p
            .Add(c => c.Config, new OpcUaEndpointConfig())
            .Add(c => c.IsLegacy, true));

        Assert.Contains("alert-warning", cut.Markup);
        Assert.Contains("migrated from a legacy format", cut.Markup);
    }

    // ── Layer E: new editor sections ──

    [Fact]
    public void Renders_Authentication_Section_Label()
    {
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, new OpcUaEndpointConfig()));
        Assert.Contains("Authentication", cut.Markup);
    }

    [Fact]
    public void EnableAuthentication_CreatesUserIdentitySubObject()
    {
        var config = new OpcUaEndpointConfig();
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        Assert.Null(config.UserIdentity);
        cut.FindAll("button").First(b => b.TextContent.Contains("Enable Authentication")).Click();

        Assert.NotNull(config.UserIdentity);
        Assert.Equal(OpcUaUserTokenType.Anonymous, config.UserIdentity!.TokenType);
    }

    [Fact]
    public void RemoveAuthentication_NullsUserIdentity()
    {
        var config = new OpcUaEndpointConfig
        {
            UserIdentity = new OpcUaUserIdentityConfig
            {
                TokenType = OpcUaUserTokenType.UsernamePassword,
                Username = "alice",
                Password = "secret"
            }
        };
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        cut.FindAll("button").First(b => b.TextContent.Contains("Remove Authentication")).Click();

        Assert.Null(config.UserIdentity);
    }

    [Fact]
    public void UsernamePassword_RendersUsernameAndPasswordInputs()
    {
        var config = new OpcUaEndpointConfig
        {
            UserIdentity = new OpcUaUserIdentityConfig
            {
                TokenType = OpcUaUserTokenType.UsernamePassword
            }
        };
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        // <label>Username</label> only renders for the UsernamePassword branch
        Assert.Contains(">Username<", cut.Markup);
        Assert.Contains(">Password<", cut.Markup);
        Assert.DoesNotContain(">Certificate path<", cut.Markup);
    }

    [Fact]
    public void X509Certificate_RendersCertificateFields()
    {
        var config = new OpcUaEndpointConfig
        {
            UserIdentity = new OpcUaUserIdentityConfig
            {
                TokenType = OpcUaUserTokenType.X509Certificate
            }
        };
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        Assert.Contains(">Certificate path<", cut.Markup);
        Assert.Contains(">Certificate password<", cut.Markup);
        Assert.DoesNotContain(">Username<", cut.Markup);
    }

    [Fact]
    public void AnonymousTokenType_ShowsNoExtraFields()
    {
        var config = new OpcUaEndpointConfig
        {
            UserIdentity = new OpcUaUserIdentityConfig { TokenType = OpcUaUserTokenType.Anonymous }
        };
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        Assert.DoesNotContain(">Username<", cut.Markup);
        Assert.DoesNotContain(">Certificate path<", cut.Markup);
    }

    [Fact]
    public void EnableDeadband_CreatesDeadbandSubObject()
    {
        var config = new OpcUaEndpointConfig();
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        Assert.Null(config.Deadband);
        cut.FindAll("button").First(b => b.TextContent.Contains("Enable Deadband")).Click();

        Assert.NotNull(config.Deadband);
    }

    [Fact]
    public void RemoveDeadband_NullsDeadband()
    {
        var config = new OpcUaEndpointConfig
        {
            Deadband = new OpcUaDeadbandConfig { Type = OpcUaDeadbandType.Percent, Value = 1.5 }
        };
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        cut.FindAll("button").First(b => b.TextContent.Contains("Remove Deadband")).Click();

        Assert.Null(config.Deadband);
    }

    [Fact]
    public void AdvancedSubscription_Section_Renders()
    {
        var config = new OpcUaEndpointConfig();
        var cut = Render<OpcUaEndpointEditor>(p => p.Add(c => c.Config, config));

        Assert.Contains("Discard oldest", cut.Markup);
        Assert.Contains("Subscription display name", cut.Markup);
        Assert.Contains("Subscription priority", cut.Markup);
        Assert.Contains("Timestamps to return", cut.Markup);
    }

    [Fact]
    public void UserIdentityError_RendersPerFieldUnderUsername()
    {
        var config = new OpcUaEndpointConfig
        {
            UserIdentity = new OpcUaUserIdentityConfig
            {
                TokenType = OpcUaUserTokenType.UsernamePassword,
                Username = ""
            }
        };
        var errors = OpcUaEndpointConfigValidator.Validate(config, "Primary.");
        var cut = Render<OpcUaEndpointEditor>(p => p
            .Add(c => c.Config, config)
            .Add(c => c.Errors, errors));

        Assert.Contains("Username is required", cut.Markup);
    }
}
