using System.Text.Json.Serialization;

namespace NATS.Server.Auth.Jwt;

/// <summary>
/// Represents the claims in a NATS user JWT.
/// Contains standard JWT fields (sub, iss, iat, exp) and a NATS-specific nested object
/// with user permissions, bearer token flags, and connection restrictions.
/// </summary>
/// <remarks>
/// Reference: github.com/nats-io/jwt/v2 — UserClaims, User, Permission types
/// </remarks>
public sealed class UserClaims
{
    /// <summary>Subject — the user's NKey public key.</summary>
    [JsonPropertyName("sub")]
    public string? Subject { get; set; }

    /// <summary>Issuer — the account or signing key that issued this JWT.</summary>
    [JsonPropertyName("iss")]
    public string? Issuer { get; set; }

    /// <summary>Issued-at time as Unix epoch seconds.</summary>
    [JsonPropertyName("iat")]
    public long IssuedAt { get; set; }

    /// <summary>Expiration time as Unix epoch seconds. 0 means no expiry.</summary>
    [JsonPropertyName("exp")]
    public long Expires { get; set; }

    /// <summary>Human-readable name for the user.</summary>
    [JsonPropertyName("name")]
    public string? Name { get; set; }

    /// <summary>NATS-specific user claims.</summary>
    [JsonPropertyName("nats")]
    public UserNats? Nats { get; set; }

    // =========================================================================
    // Convenience properties that delegate to the Nats sub-object
    // =========================================================================

    /// <summary>Whether this is a bearer token (no client nonce signature required).</summary>
    [JsonIgnore]
    public bool BearerToken => Nats?.BearerToken ?? false;

    /// <summary>The account NKey public key that issued this user JWT.</summary>
    [JsonIgnore]
    public string? IssuerAccount => Nats?.IssuerAccount;

    // =========================================================================
    // Expiry helpers
    // =========================================================================

    /// <summary>
    /// Returns true if the JWT has expired. A zero Expires value means no expiry.
    /// </summary>
    public bool IsExpired()
    {
        if (Expires == 0)
            return false;
        return DateTimeOffset.UtcNow.ToUnixTimeSeconds() > Expires;
    }

    /// <summary>
    /// Returns the expiry as a <see cref="DateTimeOffset"/>, or null if there is no expiry (Expires == 0).
    /// </summary>
    public DateTimeOffset? GetExpiry()
    {
        if (Expires == 0)
            return null;
        return DateTimeOffset.FromUnixTimeSeconds(Expires);
    }
}

/// <summary>
/// NATS-specific portion of user JWT claims.
/// Contains permissions, bearer token flag, connection restrictions, and more.
/// </summary>
public sealed class UserNats
{
    /// <summary>Publish permission with allow/deny subject lists.</summary>
    [JsonPropertyName("pub")]
    public JwtSubjectPermission? Pub { get; set; }

    /// <summary>Subscribe permission with allow/deny subject lists.</summary>
    [JsonPropertyName("sub")]
    public JwtSubjectPermission? Sub { get; set; }

    /// <summary>Response permission controlling request-reply behavior.</summary>
    [JsonPropertyName("resp")]
    public JwtResponsePermission? Resp { get; set; }

    /// <summary>Whether this is a bearer token (no nonce signature required).</summary>
    [JsonPropertyName("bearer_token")]
    public bool BearerToken { get; set; }

    /// <summary>The account NKey public key that issued this user JWT.</summary>
    [JsonPropertyName("issuer_account")]
    public string? IssuerAccount { get; set; }

    /// <summary>Tags associated with this user.</summary>
    [JsonPropertyName("tags")]
    public string[]? Tags { get; set; }

    /// <summary>Allowed source CIDRs for this user's connections.</summary>
    [JsonPropertyName("src")]
    public string[]? Src { get; set; }

    /// <summary>Allowed connection types (e.g., "STANDARD", "WEBSOCKET", "LEAFNODE").</summary>
    [JsonPropertyName("allowed_connection_types")]
    public string[]? AllowedConnectionTypes { get; set; }

    /// <summary>Time-of-day restrictions for when the user may connect.</summary>
    [JsonPropertyName("times")]
    public JwtTimeRange[]? Times { get; set; }

    /// <summary>Claim type (e.g., "user").</summary>
    [JsonPropertyName("type")]
    public string? Type { get; set; }

    /// <summary>Claim version.</summary>
    [JsonPropertyName("version")]
    public int Version { get; set; }
}

/// <summary>
/// Subject permission with allow and deny lists, as used in NATS JWTs.
/// </summary>
public sealed class JwtSubjectPermission
{
    /// <summary>Subjects the user is allowed to publish/subscribe to.</summary>
    [JsonPropertyName("allow")]
    public string[]? Allow { get; set; }

    /// <summary>Subjects the user is denied from publishing/subscribing to.</summary>
    [JsonPropertyName("deny")]
    public string[]? Deny { get; set; }
}

/// <summary>
/// Response permission controlling request-reply behavior in NATS JWTs.
/// </summary>
public sealed class JwtResponsePermission
{
    /// <summary>Maximum number of response messages allowed.</summary>
    [JsonPropertyName("max")]
    public int MaxMsgs { get; set; }

    /// <summary>Time-to-live for the response permission, in nanoseconds.</summary>
    [JsonPropertyName("ttl")]
    public long TtlNanos { get; set; }

    /// <summary>
    /// Convenience property: converts <see cref="TtlNanos"/> to a <see cref="TimeSpan"/>.
    /// </summary>
    [JsonIgnore]
    public TimeSpan Ttl => TimeSpan.FromTicks(TtlNanos / 100); // 1 tick = 100 nanoseconds
}

/// <summary>
/// A time-of-day range for connection restrictions.
/// </summary>
public sealed class JwtTimeRange
{
    /// <summary>Start time in HH:mm:ss format.</summary>
    [JsonPropertyName("start")]
    public string? Start { get; set; }

    /// <summary>End time in HH:mm:ss format.</summary>
    [JsonPropertyName("end")]
    public string? End { get; set; }
}
