using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography;

namespace StellaOps.Authority.Signing;

internal sealed class FileAuthoritySigningKeySource : IAuthoritySigningKeySource
{
    private readonly ILogger<FileAuthoritySigningKeySource> logger;

    public FileAuthoritySigningKeySource(ILogger<FileAuthoritySigningKeySource> logger)
    {
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public bool CanLoad(string source)
        => string.Equals(source, "file", StringComparison.OrdinalIgnoreCase);

    public CryptoSigningKey Load(AuthoritySigningKeyRequest request)
    {
        ArgumentNullException.ThrowIfNull(request);

        var path = ResolvePath(request.BasePath, request.Location);
        if (!File.Exists(path))
        {
            throw new FileNotFoundException($"Authority signing key '{request.KeyId}' not found.", path);
        }

        var pem = File.ReadAllText(path);

        using var ecdsa = ECDsa.Create();
        try
        {
            ecdsa.ImportFromPem(pem);
        }
        catch (CryptographicException ex)
        {
            logger.LogError(ex, "Failed to load Authority signing key {KeyId} from {Path}.", request.KeyId, path);
            throw new InvalidOperationException("Failed to import Authority signing key. Ensure the PEM is an unencrypted EC private key.", ex);
        }

        var parameters = ecdsa.ExportParameters(includePrivateParameters: true);

        var metadata = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
        {
            ["source"] = Path.GetFullPath(path),
            ["loader"] = "file",
            ["status"] = request.Status
        };

        if (!string.IsNullOrWhiteSpace(request.Provider))
        {
            metadata["provider"] = request.Provider;
        }

        if (request.AdditionalMetadata is not null)
        {
            foreach (var pair in request.AdditionalMetadata)
            {
                metadata[pair.Key] = pair.Value;
            }
        }

        metadata["status"] = request.Status;

        logger.LogInformation("Loaded Authority signing key {KeyId} from {Path}.", request.KeyId, path);

        return new CryptoSigningKey(
            new CryptoKeyReference(request.KeyId, request.Provider),
            request.Algorithm,
            in parameters,
            request.CreatedAt ?? DateTimeOffset.UtcNow,
            request.ExpiresAt,
            metadata);
    }

    private static string ResolvePath(string basePath, string location)
    {
        if (string.IsNullOrWhiteSpace(location))
        {
            throw new InvalidOperationException("Signing key location is required.");
        }

        if (Path.IsPathRooted(location))
        {
            return location;
        }

        if (string.IsNullOrWhiteSpace(basePath))
        {
            return Path.GetFullPath(location);
        }

        return Path.GetFullPath(Path.Combine(basePath, location));
    }
}
