// Copyright 2022-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 System.Security.Cryptography.X509Certificates;

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

/// <summary>
/// Windows certificate store location.
/// Mirrors the Go certstore <c>StoreType</c> enum (windowsCurrentUser=1, windowsLocalMachine=2).
/// </summary>
public enum StoreType
{
    Empty = 0,
    WindowsCurrentUser = 1,
    WindowsLocalMachine = 2,
}

/// <summary>
/// Certificate lookup criterion.
/// Mirrors the Go certstore <c>MatchByType</c> enum (matchByIssuer=1, matchBySubject=2, matchByThumbprint=3).
/// </summary>
public enum MatchByType
{
    Empty = 0,
    Issuer = 1,
    Subject = 2,
    Thumbprint = 3,
}

/// <summary>
/// Result returned by <see cref="CertificateStoreService.TLSConfig"/>.
/// Mirrors the data that the Go <c>TLSConfig</c> populates into <c>*tls.Config</c>.
/// </summary>
public sealed class CertStoreTlsResult
{
    public CertStoreTlsResult(X509Certificate2 leaf, X509Certificate2Collection? caCerts = null)
    {
        Leaf = leaf;
        CaCerts = caCerts;
    }

    /// <summary>The leaf certificate (with private key) to use as the server/client identity.</summary>
    public X509Certificate2 Leaf { get; }

    /// <summary>Optional pool of CA certificates used to validate client certificates (mTLS).</summary>
    public X509Certificate2Collection? CaCerts { get; }
}

/// <summary>
/// Error constants for the Windows certificate store module.
/// Mirrors certstore/errors.go.
/// </summary>
public static class CertStoreErrors
{
    public static readonly InvalidOperationException ErrBadCryptoStoreProvider =
        new("unable to open certificate store or store not available");

    public static readonly InvalidOperationException ErrBadRSAHashAlgorithm =
        new("unsupported RSA hash algorithm");

    public static readonly InvalidOperationException ErrBadSigningAlgorithm =
        new("unsupported signing algorithm");

    public static readonly InvalidOperationException ErrStoreRSASigningError =
        new("unable to obtain RSA signature from store");

    public static readonly InvalidOperationException ErrStoreECDSASigningError =
        new("unable to obtain ECDSA signature from store");

    public static readonly InvalidOperationException ErrNoPrivateKeyStoreRef =
        new("unable to obtain private key handle from store");

    public static readonly InvalidOperationException ErrExtractingPrivateKeyMetadata =
        new("unable to extract private key metadata");

    public static readonly InvalidOperationException ErrExtractingECCPublicKey =
        new("unable to extract ECC public key from store");

    public static readonly InvalidOperationException ErrExtractingRSAPublicKey =
        new("unable to extract RSA public key from store");

    public static readonly InvalidOperationException ErrExtractingPublicKey =
        new("unable to extract public key from store");

    public static readonly InvalidOperationException ErrBadPublicKeyAlgorithm =
        new("unsupported public key algorithm");

    public static readonly InvalidOperationException ErrExtractPropertyFromKey =
        new("unable to extract property from key");

    public static readonly InvalidOperationException ErrBadECCCurveName =
        new("unsupported ECC curve name");

    public static readonly InvalidOperationException ErrFailedCertSearch =
        new("unable to find certificate in store");

    public static readonly InvalidOperationException ErrFailedX509Extract =
        new("unable to extract x509 from certificate");

    public static readonly InvalidOperationException ErrBadMatchByType =
        new("cert match by type not implemented");

    public static readonly InvalidOperationException ErrBadCertStore =
        new("cert store type not implemented");

    public static readonly InvalidOperationException ErrConflictCertFileAndStore =
        new("'cert_file' and 'cert_store' may not both be configured");

    public static readonly InvalidOperationException ErrBadCertStoreField =
        new("expected 'cert_store' to be a valid non-empty string");

    public static readonly InvalidOperationException ErrBadCertMatchByField =
        new("expected 'cert_match_by' to be a valid non-empty string");

    public static readonly InvalidOperationException ErrBadCertMatchField =
        new("expected 'cert_match' to be a valid non-empty string");

    public static readonly InvalidOperationException ErrBadCaCertMatchField =
        new("expected 'ca_certs_match' to be a valid non-empty string array");

    public static readonly InvalidOperationException ErrBadCertMatchSkipInvalidField =
        new("expected 'cert_match_skip_invalid' to be a boolean");

    public static readonly InvalidOperationException ErrOSNotCompatCertStore =
        new("cert_store not compatible with current operating system");
}
