// Copyright 2012-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Shouldly;
using ZB.MOM.NatsNet.Server.Auth;

namespace ZB.MOM.NatsNet.Server.Tests.Auth;

/// <summary>
/// Tests for auth type cloning and validation.
/// Mirrors Go auth_test.go: TestUserClone*, TestDNSAltNameMatching, and additional unit tests.
/// </summary>
public class AuthTypesTests
{
    // -------------------------------------------------------------------------
    // TestUserCloneNilPermissions — Go test ID 142
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_NilPermissions_ProducesDeepCopy()
    {
        var user = new User { Username = "foo", Password = "bar" };
        var clone = user.Clone()!;

        clone.Username.ShouldBe(user.Username);
        clone.Password.ShouldBe(user.Password);
        clone.Permissions.ShouldBeNull();

        // Mutation should not affect original.
        clone.Password = "baz";
        user.Password.ShouldBe("bar");
    }

    // -------------------------------------------------------------------------
    // TestUserClone — Go test ID 143
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_WithPermissions_ProducesDeepCopy()
    {
        var user = new User
        {
            Username = "foo",
            Password = "bar",
            Permissions = new Permissions
            {
                Publish = new SubjectPermission { Allow = ["foo"] },
                Subscribe = new SubjectPermission { Allow = ["bar"] },
            },
        };
        var clone = user.Clone()!;

        clone.Username.ShouldBe("foo");
        clone.Permissions.ShouldNotBeNull();
        clone.Permissions!.Publish!.Allow.ShouldBe(["foo"]);
        clone.Permissions.Subscribe!.Allow.ShouldBe(["bar"]);

        // Mutating clone should not affect original.
        clone.Permissions.Subscribe.Allow = ["baz"];
        user.Permissions!.Subscribe!.Allow.ShouldBe(["bar"]);
    }

    // -------------------------------------------------------------------------
    // TestUserClonePermissionsNoLists — Go test ID 144
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_EmptyPermissions_PublishSubscribeAreNull()
    {
        var user = new User
        {
            Username = "foo",
            Password = "bar",
            Permissions = new Permissions(),
        };
        var clone = user.Clone()!;

        clone.Permissions.ShouldNotBeNull();
        clone.Permissions!.Publish.ShouldBeNull();
        clone.Permissions.Subscribe.ShouldBeNull();
    }

    // -------------------------------------------------------------------------
    // TestUserCloneNoPermissions — Go test ID 145
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_NoPermissions_PermissionsIsNull()
    {
        var user = new User { Username = "foo", Password = "bar" };
        var clone = user.Clone()!;
        clone.Permissions.ShouldBeNull();
    }

    // -------------------------------------------------------------------------
    // TestUserCloneNil — Go test ID 146
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_NilUser_ReturnsNull()
    {
        User? user = null;
        var clone = user?.Clone();
        clone.ShouldBeNull();
    }

    // -------------------------------------------------------------------------
    // NkeyUser clone tests (additional coverage for NkeyUser.clone)
    // -------------------------------------------------------------------------

    [Fact]
    public void NkeyUserClone_WithAllowedConnectionTypes_ProducesDeepCopy()
    {
        var nkey = new NkeyUser
        {
            Nkey = "NKEY123",
            SigningKey = "SK1",
            AllowedConnectionTypes = new HashSet<string> { "STANDARD", "WEBSOCKET" },
            Permissions = new Permissions
            {
                Publish = new SubjectPermission { Allow = ["pub.>"] },
            },
        };
        var clone = nkey.Clone()!;

        clone.Nkey.ShouldBe("NKEY123");
        clone.AllowedConnectionTypes.ShouldNotBeNull();
        clone.AllowedConnectionTypes!.Count.ShouldBe(2);
        clone.Permissions!.Publish!.Allow.ShouldBe(["pub.>"]);

        // Mutating clone should not affect original.
        clone.AllowedConnectionTypes.Add("MQTT");
        nkey.AllowedConnectionTypes.Count.ShouldBe(2);

        clone.Permissions.Publish.Allow = ["other.>"];
        nkey.Permissions!.Publish!.Allow.ShouldBe(["pub.>"]);
    }

    [Fact]
    public void NkeyUserClone_NilNkeyUser_ReturnsNull()
    {
        NkeyUser? nkey = null;
        var clone = nkey?.Clone();
        clone.ShouldBeNull();
    }

    // -------------------------------------------------------------------------
    // SubjectPermission clone tests
    // -------------------------------------------------------------------------

    [Fact]
    public void SubjectPermissionClone_WithAllowAndDeny_ProducesDeepCopy()
    {
        var perm = new SubjectPermission
        {
            Allow = ["foo.>", "bar.>"],
            Deny = ["baz.>"],
        };
        var clone = perm.Clone();

        clone.Allow.ShouldBe(["foo.>", "bar.>"]);
        clone.Deny.ShouldBe(["baz.>"]);

        clone.Allow.Add("extra");
        perm.Allow.Count.ShouldBe(2);
    }

    [Fact]
    public void SubjectPermissionClone_NullLists_StaysNull()
    {
        var perm = new SubjectPermission();
        var clone = perm.Clone();
        clone.Allow.ShouldBeNull();
        clone.Deny.ShouldBeNull();
    }

    // -------------------------------------------------------------------------
    // Permissions clone with Response
    // -------------------------------------------------------------------------

    [Fact]
    public void PermissionsClone_WithResponse_ProducesDeepCopy()
    {
        var perms = new Permissions
        {
            Publish = new SubjectPermission { Allow = ["pub"] },
            Subscribe = new SubjectPermission { Deny = ["deny"] },
            Response = new ResponsePermission { MaxMsgs = 5, Expires = TimeSpan.FromMinutes(1) },
        };
        var clone = perms.Clone();

        clone.Response.ShouldNotBeNull();
        clone.Response!.MaxMsgs.ShouldBe(5);
        clone.Response.Expires.ShouldBe(TimeSpan.FromMinutes(1));

        // Mutating clone should not affect original.
        clone.Response.MaxMsgs = 10;
        perms.Response!.MaxMsgs.ShouldBe(5);
    }

    // -------------------------------------------------------------------------
    // User clone with AllowedConnectionTypes
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_WithAllowedConnectionTypes_ProducesDeepCopy()
    {
        var user = new User
        {
            Username = "u1",
            AllowedConnectionTypes = new HashSet<string> { "STANDARD" },
        };
        var clone = user.Clone()!;

        clone.AllowedConnectionTypes.ShouldNotBeNull();
        clone.AllowedConnectionTypes!.ShouldContain("STANDARD");

        clone.AllowedConnectionTypes.Add("MQTT");
        user.AllowedConnectionTypes.Count.ShouldBe(1);
    }

    // -------------------------------------------------------------------------
    // User clone with Account (shared by reference)
    // -------------------------------------------------------------------------

    [Fact]
    public void UserClone_AccountSharedByReference()
    {
        var acct = new Account { Name = "TestAccount" };
        var user = new User { Username = "u1", Account = acct };
        var clone = user.Clone()!;

        // Account should be same reference.
        clone.Account.ShouldBeSameAs(acct);
    }
}
