// Copyright 2021-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/ocsp.go, server/ocsp_peer.go, server/ocsp_responsecache.go
// in the NATS server Go source.

using System.Security.Cryptography.X509Certificates;

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

/// <summary>
/// Controls how OCSP stapling behaves for a TLS certificate.
/// Mirrors Go <c>OCSPMode uint8</c> in server/ocsp.go.
/// </summary>
public enum OcspMode : byte
{
    /// <summary>
    /// Staple only if the "status_request" OID is present in the certificate.
    /// Mirrors Go <c>OCSPModeAuto</c>.
    /// </summary>
    Auto      = 0,

    /// <summary>
    /// Must staple — honors the Must-Staple flag and shuts down on revocation.
    /// Mirrors Go <c>OCSPModeMust</c>.
    /// </summary>
    MustStaple = 1,

    /// <summary>
    /// Always obtain OCSP status, regardless of certificate flags.
    /// Mirrors Go <c>OCSPModeAlways</c>.
    /// </summary>
    Always    = 2,

    /// <summary>
    /// Never check OCSP, even if the certificate has the Must-Staple flag.
    /// Mirrors Go <c>OCSPModeNever</c>.
    /// </summary>
    Never     = 3,
}

/// <summary>
/// Holds a cached OCSP staple response and its expiry information.
/// </summary>
internal sealed class OcspStaple
{
    /// <summary>The raw DER-encoded OCSP response bytes.</summary>
    public byte[]?  Response   { get; set; }

    /// <summary>When the OCSP response next needs to be refreshed.</summary>
    public DateTime NextUpdate { get; set; }
}

/// <summary>
/// Orchestrates OCSP stapling for a single TLS certificate.
/// Monitors certificate validity and refreshes the staple on a background timer.
/// Mirrors Go <c>OCSPMonitor</c> struct in server/ocsp.go.
/// Replaces the stub in NatsServerTypes.cs.
/// </summary>
internal sealed class OcspMonitor
{
    private readonly Lock _mu = new();

    /// <summary>Path to the TLS certificate file being monitored.</summary>
    public string?    CertFile       { get; set; }

    /// <summary>Path to the CA certificate file used to verify OCSP responses.</summary>
    public string?    CaFile         { get; set; }

    /// <summary>Path to a persisted OCSP staple file (optional).</summary>
    public string?    OcspStapleFile { get; set; }

    /// <summary>The OCSP stapling mode for this monitor.</summary>
    public OcspMode   Mode           { get; set; }

    /// <summary>How often to check for a fresh OCSP response.</summary>
    public TimeSpan   CheckInterval  { get; set; } = TimeSpan.FromHours(24);

    /// <summary>The owning server instance.</summary>
    public NatsServer? Server        { get; set; }

    /// <summary>The synchronisation lock for this monitor's mutable state.</summary>
    public Lock Mu => _mu;

    /// <summary>Starts the background OCSP refresh timer.</summary>
    public void Start()
        => throw new NotImplementedException("TODO: session 23 — ocsp");

    /// <summary>Stops the background OCSP refresh timer.</summary>
    public void Stop()
        => throw new NotImplementedException("TODO: session 23 — ocsp");

    /// <summary>Returns the current cached OCSP staple bytes, or <c>null</c> if none.</summary>
    public byte[]? GetStaple()
        => throw new NotImplementedException("TODO: session 23 — ocsp");
}

/// <summary>
/// Interface for caching raw OCSP response bytes keyed by certificate fingerprint.
/// Mirrors Go <c>OCSPResponseCache</c> interface in server/ocsp_responsecache.go.
/// Replaces the stub in NatsServerTypes.cs.
/// </summary>
public interface IOcspResponseCache
{
    /// <summary>Returns the cached OCSP response for <paramref name="key"/>, or <c>null</c>.</summary>
    byte[]? Get(string key);

    /// <summary>Stores an OCSP response under <paramref name="key"/>.</summary>
    void    Put(string key, byte[] response);

    /// <summary>Removes the cached entry for <paramref name="key"/>.</summary>
    void    Remove(string key);
}

/// <summary>
/// A no-op OCSP cache that never stores anything.
/// Mirrors Go <c>NoOpCache</c> in server/ocsp_responsecache.go.
/// </summary>
internal sealed class NoOpCache : IOcspResponseCache
{
    public byte[]? Get(string key)             => null;
    public void    Put(string key, byte[] response) { }
    public void    Remove(string key)          { }
}

/// <summary>
/// An OCSP cache backed by a local directory on disk.
/// Mirrors Go <c>LocalCache</c> in server/ocsp_responsecache.go.
/// Full implementation is deferred to session 23.
/// </summary>
internal sealed class LocalDirCache : IOcspResponseCache
{
    private readonly string _dir;

    public LocalDirCache(string dir)
    {
        _dir = dir;
    }

    public byte[]? Get(string key)
        => throw new NotImplementedException("TODO: session 23 — ocsp");

    public void Put(string key, byte[] response)
        => throw new NotImplementedException("TODO: session 23 — ocsp");

    public void Remove(string key)
        => throw new NotImplementedException("TODO: session 23 — ocsp");
}

/// <summary>
/// Payload for the OCSP peer certificate rejection advisory event.
/// Mirrors Go <c>OCSPPeerRejectEventMsg</c> fields in server/events.go
/// and the OCSP peer reject logic in server/ocsp_peer.go.
/// </summary>
public sealed class OcspPeerRejectInfo
{
    [System.Text.Json.Serialization.JsonPropertyName("peer")]
    public string Peer   { get; set; } = string.Empty;

    [System.Text.Json.Serialization.JsonPropertyName("reason")]
    public string Reason { get; set; } = string.Empty;
}
