using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Text.Json.Serialization;

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

/// <summary>OCSP certificate status values.</summary>
/// <remarks>Mirrors the Go <c>ocsp.Good/Revoked/Unknown</c> constants (0/1/2).</remarks>
[JsonConverter(typeof(OcspStatusAssertionJsonConverter))]
public enum OcspStatusAssertion
{
    Good = 0,
    Revoked = 1,
    Unknown = 2,
}

/// <summary>JSON converter: serializes <see cref="OcspStatusAssertion"/> as lowercase string.</summary>
public sealed class OcspStatusAssertionJsonConverter : JsonConverter<OcspStatusAssertion>
{
    private static readonly IReadOnlyDictionary<string, OcspStatusAssertion> StrToVal =
        new Dictionary<string, OcspStatusAssertion>(StringComparer.OrdinalIgnoreCase)
        {
            ["good"] = OcspStatusAssertion.Good,
            ["revoked"] = OcspStatusAssertion.Revoked,
            ["unknown"] = OcspStatusAssertion.Unknown,
        };

    private static readonly IReadOnlyDictionary<OcspStatusAssertion, string> ValToStr =
        new Dictionary<OcspStatusAssertion, string>
        {
            [OcspStatusAssertion.Good] = "good",
            [OcspStatusAssertion.Revoked] = "revoked",
            [OcspStatusAssertion.Unknown] = "unknown",
        };

    public override OcspStatusAssertion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var s = reader.GetString() ?? string.Empty;
        return StrToVal.TryGetValue(s, out var v) ? v : OcspStatusAssertion.Unknown;
    }

    public override void Write(Utf8JsonWriter writer, OcspStatusAssertion value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(ValToStr.TryGetValue(value, out var s) ? s : "unknown");
    }
}

/// <summary>
/// Returns the string representation of an OCSP status integer.
/// Falls back to "unknown" for unrecognized values (never defaults to "good").
/// </summary>
public static class OcspStatusAssertionExtensions
{
    public static string GetStatusAssertionStr(int statusInt) => statusInt switch
    {
        0 => "good",
        1 => "revoked",
        _ => "unknown",
    };
}

/// <summary>Parsed OCSP peer configuration.</summary>
public sealed class OcspPeerConfig
{
    public static readonly TimeSpan DefaultAllowedClockSkew = TimeSpan.FromSeconds(30);
    public static readonly TimeSpan DefaultOCSPResponderTimeout = TimeSpan.FromSeconds(2);
    public static readonly TimeSpan DefaultTTLUnsetNextUpdate = TimeSpan.FromHours(1);

    public bool Verify { get; set; } = false;
    public double Timeout { get; set; } = DefaultOCSPResponderTimeout.TotalSeconds;
    public double ClockSkew { get; set; } = DefaultAllowedClockSkew.TotalSeconds;
    public bool WarnOnly { get; set; } = false;
    public bool UnknownIsGood { get; set; } = false;
    public bool AllowWhenCAUnreachable { get; set; } = false;
    public double TTLUnsetNextUpdate { get; set; } = DefaultTTLUnsetNextUpdate.TotalSeconds;

    /// <summary>Returns a new <see cref="OcspPeerConfig"/> with defaults populated.</summary>
    public static OcspPeerConfig Create() => new();
}

/// <summary>
/// Represents a certificate chain link: a leaf certificate and its issuer,
/// plus the OCSP web endpoints parsed from the leaf's AIA extension.
/// </summary>
public sealed class ChainLink
{
    public X509Certificate2? Leaf { get; set; }
    public X509Certificate2? Issuer { get; set; }
    public IReadOnlyList<Uri>? OcspWebEndpoints { get; set; }
}

/// <summary>
/// Parsed OCSP response data. Mirrors the fields of <c>golang.org/x/crypto/ocsp.Response</c>
/// needed by <see cref="OcspUtilities"/>.
/// </summary>
/// <remarks>
/// Full OCSP response parsing (DER/ASN.1) requires an additional library (e.g. Bouncy Castle).
/// This type represents the already-parsed response for use in validation and caching logic.
/// </remarks>
public sealed class OcspResponse
{
    public OcspStatusAssertion Status { get; init; }
    public DateTime ThisUpdate { get; init; }
    /// <summary><see cref="DateTime.MinValue"/> means "not set" (CA did not supply NextUpdate).</summary>
    public DateTime NextUpdate { get; init; }
    /// <summary>Raw OCSP response payload bytes when available.</summary>
    public byte[]? Raw { get; init; }
    /// <summary>Optional delegated signer certificate (RFC 6960 §4.2.2.2).</summary>
    public X509Certificate2? Certificate { get; init; }
}

/// <summary>Neutral logging interface for plugin use. Mirrors the Go <c>certidp.Log</c> struct.</summary>
public sealed class OcspLog
{
    public Action<string, object[]>? Debugf { get; set; }
    public Action<string, object[]>? Noticef { get; set; }
    public Action<string, object[]>? Warnf { get; set; }
    public Action<string, object[]>? Errorf { get; set; }
    public Action<string, object[]>? Tracef { get; set; }

    internal void Debug(string format, params object[] args) => Debugf?.Invoke(format, args);
}

/// <summary>JSON-serializable certificate information.</summary>
public sealed class CertInfo
{
    [JsonPropertyName("subject")] public string? Subject { get; init; }
    [JsonPropertyName("issuer")] public string? Issuer { get; init; }
    [JsonPropertyName("fingerprint")] public string? Fingerprint { get; init; }
    [JsonPropertyName("raw")] public byte[]? Raw { get; init; }
}
