using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace StellaOps.Cryptography;
/// 
/// Default implementation of  with deterministic provider ordering.
/// 
public sealed class CryptoProviderRegistry : ICryptoProviderRegistry
{
    private readonly ReadOnlyCollection providers;
    private readonly IReadOnlyDictionary providersByName;
    private readonly IReadOnlyList preferredOrder;
    private readonly HashSet preferredOrderSet;
    public CryptoProviderRegistry(
        IEnumerable providers,
        IEnumerable? preferredProviderOrder = null)
    {
        if (providers is null)
        {
            throw new ArgumentNullException(nameof(providers));
        }
        var providerList = providers.ToList();
        if (providerList.Count == 0)
        {
            throw new ArgumentException("At least one crypto provider must be registered.", nameof(providers));
        }
        providersByName = providerList.ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
        this.providers = new ReadOnlyCollection(providerList);
        preferredOrder = preferredProviderOrder?
            .Where(name => providersByName.ContainsKey(name))
            .Select(name => providersByName[name].Name)
            .ToArray() ?? Array.Empty();
        preferredOrderSet = new HashSet(preferredOrder, StringComparer.OrdinalIgnoreCase);
    }
    public IReadOnlyCollection Providers => providers;
    public bool TryResolve(string preferredProvider, out ICryptoProvider provider)
    {
        if (string.IsNullOrWhiteSpace(preferredProvider))
        {
            provider = default!;
            return false;
        }
        return providersByName.TryGetValue(preferredProvider, out provider!);
    }
    public ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId)
    {
        if (string.IsNullOrWhiteSpace(algorithmId))
        {
            throw new ArgumentException("Algorithm identifier is required.", nameof(algorithmId));
        }
        foreach (var provider in EnumerateCandidates())
        {
            if (provider.Supports(capability, algorithmId))
            {
                return provider;
            }
        }
        throw new InvalidOperationException(
            $"No crypto provider is registered for capability '{capability}' and algorithm '{algorithmId}'.");
    }
    public CryptoSignerResolution ResolveSigner(
        CryptoCapability capability,
        string algorithmId,
        CryptoKeyReference keyReference,
        string? preferredProvider = null)
    {
        if (!string.IsNullOrWhiteSpace(preferredProvider) &&
            providersByName.TryGetValue(preferredProvider!, out var hinted))
        {
            if (!hinted.Supports(capability, algorithmId))
            {
                throw new InvalidOperationException(
                    $"Provider '{preferredProvider}' does not support capability '{capability}' and algorithm '{algorithmId}'.");
            }
            var signer = hinted.GetSigner(algorithmId, keyReference);
            return new CryptoSignerResolution(signer, hinted.Name);
        }
        var provider = ResolveOrThrow(capability, algorithmId);
        var resolved = provider.GetSigner(algorithmId, keyReference);
        return new CryptoSignerResolution(resolved, provider.Name);
    }
    private IEnumerable EnumerateCandidates()
    {
        foreach (var name in preferredOrder)
        {
            yield return providersByName[name];
        }
        foreach (var provider in providers)
        {
            if (!preferredOrderSet.Contains(provider.Name))
            {
                yield return provider;
            }
        }
    }
}