using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Security.Cryptography;

namespace StellaOps.Cryptography;

/// <summary>
/// Represents asymmetric signing key material managed by a crypto provider.
/// </summary>
public sealed class CryptoSigningKey
{
    private static readonly ReadOnlyDictionary<string, string?> EmptyMetadata =
        new(new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase));

    public CryptoSigningKey(
        CryptoKeyReference reference,
        string algorithmId,
        in ECParameters privateParameters,
        DateTimeOffset createdAt,
        DateTimeOffset? expiresAt = null,
        IReadOnlyDictionary<string, string?>? metadata = null)
    {
        Reference = reference ?? throw new ArgumentNullException(nameof(reference));

        if (string.IsNullOrWhiteSpace(algorithmId))
        {
            throw new ArgumentException("Algorithm identifier is required.", nameof(algorithmId));
        }

        if (privateParameters.D is null || privateParameters.D.Length == 0)
        {
            throw new ArgumentException("Private key parameters must include the scalar component.", nameof(privateParameters));
        }

        AlgorithmId = algorithmId;
        CreatedAt = createdAt;
        ExpiresAt = expiresAt;

        PrivateParameters = CloneParameters(privateParameters, includePrivate: true);
        PublicParameters = CloneParameters(privateParameters, includePrivate: false);
        Metadata = metadata is null
            ? EmptyMetadata
            : new ReadOnlyDictionary<string, string?>(metadata.ToDictionary(
                static pair => pair.Key,
                static pair => pair.Value,
                StringComparer.OrdinalIgnoreCase));
    }

    /// <summary>
    /// Gets the key reference (id + provider hint).
    /// </summary>
    public CryptoKeyReference Reference { get; }

    /// <summary>
    /// Gets the algorithm identifier (e.g., ES256).
    /// </summary>
    public string AlgorithmId { get; }

    /// <summary>
    /// Gets the private EC parameters (cloned).
    /// </summary>
    public ECParameters PrivateParameters { get; }

    /// <summary>
    /// Gets the public EC parameters (cloned, no private scalar).
    /// </summary>
    public ECParameters PublicParameters { get; }

    /// <summary>
    /// Gets the timestamp when the key was created/imported.
    /// </summary>
    public DateTimeOffset CreatedAt { get; }

    /// <summary>
    /// Gets the optional expiry timestamp for the key.
    /// </summary>
    public DateTimeOffset? ExpiresAt { get; }

    /// <summary>
    /// Gets arbitrary metadata entries associated with the key.
    /// </summary>
    public IReadOnlyDictionary<string, string?> Metadata { get; }

    private static ECParameters CloneParameters(ECParameters source, bool includePrivate)
    {
        var clone = new ECParameters
        {
            Curve = source.Curve,
            Q = new ECPoint
            {
                X = source.Q.X is null ? null : (byte[])source.Q.X.Clone(),
                Y = source.Q.Y is null ? null : (byte[])source.Q.Y.Clone()
            }
        };

        if (includePrivate && source.D is not null)
        {
            clone.D = (byte[])source.D.Clone();
        }

        return clone;
    }
}
