// 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.
//
// Adapted from server/auth.go (type definitions) in the NATS server Go source.

using ZB.MOM.NatsNet.Server;

namespace ZB.MOM.NatsNet.Server.Auth;

/// <summary>
/// Represents a user authenticated via NKey.
/// Mirrors Go <c>NkeyUser</c> struct in auth.go.
/// </summary>
public class NkeyUser
{
    public string Nkey { get; set; } = string.Empty;
    public long Issued { get; set; }
    public Permissions? Permissions { get; set; }
    public Account? Account { get; set; }
    public string SigningKey { get; set; } = string.Empty;
    public HashSet<string>? AllowedConnectionTypes { get; set; }
    public bool ProxyRequired { get; set; }

    /// <summary>
    /// Deep-clones this NkeyUser. Account is shared by reference.
    /// Mirrors Go <c>NkeyUser.clone()</c>.
    /// </summary>
    public NkeyUser? Clone()
    {
        var clone = (NkeyUser)MemberwiseClone();
        // Account is not cloned because it is always by reference to an existing struct.
        clone.Permissions = Permissions?.Clone();

        if (AllowedConnectionTypes != null)
        {
            clone.AllowedConnectionTypes = new HashSet<string>(AllowedConnectionTypes);
        }

        return clone;
    }
}

/// <summary>
/// Represents a user with username/password credentials.
/// Mirrors Go <c>User</c> struct in auth.go.
/// </summary>
public class User
{
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public Permissions? Permissions { get; set; }
    public Account? Account { get; set; }
    public DateTime ConnectionDeadline { get; set; }
    public HashSet<string>? AllowedConnectionTypes { get; set; }
    public bool ProxyRequired { get; set; }

    /// <summary>
    /// Deep-clones this User. Account is shared by reference.
    /// Mirrors Go <c>User.clone()</c>.
    /// </summary>
    public User? Clone()
    {
        var clone = (User)MemberwiseClone();
        // Account is not cloned because it is always by reference to an existing struct.
        clone.Permissions = Permissions?.Clone();

        if (AllowedConnectionTypes != null)
        {
            clone.AllowedConnectionTypes = new HashSet<string>(AllowedConnectionTypes);
        }

        return clone;
    }
}

/// <summary>
/// Subject-level allow/deny permission.
/// Mirrors Go <c>SubjectPermission</c> in auth.go.
/// </summary>
public class SubjectPermission
{
    public List<string>? Allow { get; set; }
    public List<string>? Deny { get; set; }

    /// <summary>
    /// Deep-clones this SubjectPermission.
    /// Mirrors Go <c>SubjectPermission.clone()</c>.
    /// </summary>
    public SubjectPermission Clone()
    {
        var clone = new SubjectPermission();
        if (Allow != null)
        {
            clone.Allow = new List<string>(Allow);
        }
        if (Deny != null)
        {
            clone.Deny = new List<string>(Deny);
        }
        return clone;
    }
}

/// <summary>
/// Response permission for request-reply patterns.
/// Mirrors Go <c>ResponsePermission</c> in auth.go.
/// </summary>
public class ResponsePermission
{
    public int MaxMsgs { get; set; }
    public TimeSpan Expires { get; set; }
}

/// <summary>
/// Publish/subscribe permissions container.
/// Mirrors Go <c>Permissions</c> in auth.go.
/// </summary>
public class Permissions
{
    public SubjectPermission? Publish { get; set; }
    public SubjectPermission? Subscribe { get; set; }
    public ResponsePermission? Response { get; set; }

    /// <summary>
    /// Deep-clones this Permissions struct.
    /// Mirrors Go <c>Permissions.clone()</c>.
    /// </summary>
    public Permissions Clone()
    {
        var clone = new Permissions();
        if (Publish != null)
        {
            clone.Publish = Publish.Clone();
        }
        if (Subscribe != null)
        {
            clone.Subscribe = Subscribe.Clone();
        }
        if (Response != null)
        {
            clone.Response = new ResponsePermission
            {
                MaxMsgs = Response.MaxMsgs,
                Expires = Response.Expires,
            };
        }
        return clone;
    }
}

/// <summary>
/// Route-level import/export permissions.
/// Mirrors Go <c>RoutePermissions</c> in auth.go.
/// </summary>
public class RoutePermissions
{
    public SubjectPermission? Import { get; set; }
    public SubjectPermission? Export { get; set; }
}

// Account stub removed — full implementation is in Accounts/Account.cs
// in the ZB.MOM.NatsNet.Server namespace.

/// <summary>
/// Sentinel exception representing a proxy-auth "not trusted" error.
/// Mirrors Go <c>ErrAuthProxyNotTrusted</c> in server/auth.go.
/// </summary>
public sealed class AuthProxyNotTrustedException : InvalidOperationException
{
    public AuthProxyNotTrustedException() : base("proxy not trusted") { }
}

/// <summary>
/// Sentinel exception representing a proxy-auth "required" error.
/// Mirrors Go <c>ErrAuthProxyRequired</c> in server/auth.go.
/// </summary>
public sealed class AuthProxyRequiredException : InvalidOperationException
{
    public AuthProxyRequiredException() : base("proxy required") { }
}
