using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;

namespace StellaOps.Authority.Plugins.Abstractions;

/// <summary>
/// Well-known Authority plugin capability identifiers.
/// </summary>
public static class AuthorityPluginCapabilities
{
    public const string Password = "password";
    public const string Bootstrap = "bootstrap";
    public const string Mfa = "mfa";
    public const string ClientProvisioning = "clientProvisioning";
}

/// <summary>
/// Immutable description of an Authority plugin loaded from configuration.
/// </summary>
/// <param name="Name">Logical name derived from configuration key.</param>
/// <param name="Type">Plugin type identifier (used for capability routing).</param>
/// <param name="Enabled">Whether the plugin is enabled.</param>
/// <param name="AssemblyName">Assembly name without extension.</param>
/// <param name="AssemblyPath">Explicit assembly path override.</param>
/// <param name="Capabilities">Capability hints exposed by the plugin.</param>
/// <param name="Metadata">Additional metadata forwarded to plugin implementations.</param>
/// <param name="ConfigPath">Absolute path to the plugin configuration manifest.</param>
public sealed record AuthorityPluginManifest(
    string Name,
    string Type,
    bool Enabled,
    string? AssemblyName,
    string? AssemblyPath,
    IReadOnlyList<string> Capabilities,
    IReadOnlyDictionary<string, string?> Metadata,
    string ConfigPath)
{
    /// <summary>
    /// Determines whether the manifest declares the specified capability.
    /// </summary>
    /// <param name="capability">Capability identifier to check.</param>
    public bool HasCapability(string capability)
    {
        if (string.IsNullOrWhiteSpace(capability))
        {
            return false;
        }

        foreach (var entry in Capabilities)
        {
            if (string.Equals(entry, capability, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }

        return false;
    }
}

/// <summary>
/// Runtime context combining plugin manifest metadata and its bound configuration.
/// </summary>
/// <param name="Manifest">Manifest describing the plugin.</param>
/// <param name="Configuration">Root configuration built from the plugin YAML manifest.</param>
public sealed record AuthorityPluginContext(
    AuthorityPluginManifest Manifest,
    IConfiguration Configuration);

/// <summary>
/// Registry exposing the set of Authority plugins loaded at runtime.
/// </summary>
public interface IAuthorityPluginRegistry
{
    IReadOnlyCollection<AuthorityPluginContext> Plugins { get; }

    bool TryGet(string name, [NotNullWhen(true)] out AuthorityPluginContext? context);

    AuthorityPluginContext GetRequired(string name)
    {
        if (TryGet(name, out var context))
        {
            return context;
        }

        throw new KeyNotFoundException($"Authority plugin '{name}' is not registered.");
    }
}

/// <summary>
/// Registry exposing loaded identity provider plugins and their capabilities.
/// </summary>
public interface IAuthorityIdentityProviderRegistry
{
    /// <summary>
    /// Gets all registered identity provider plugins keyed by logical name.
    /// </summary>
    IReadOnlyCollection<IIdentityProviderPlugin> Providers { get; }

    /// <summary>
    /// Gets identity providers that advertise password support.
    /// </summary>
    IReadOnlyCollection<IIdentityProviderPlugin> PasswordProviders { get; }

    /// <summary>
    /// Gets identity providers that advertise multi-factor authentication support.
    /// </summary>
    IReadOnlyCollection<IIdentityProviderPlugin> MfaProviders { get; }

    /// <summary>
    /// Gets identity providers that advertise client provisioning support.
    /// </summary>
    IReadOnlyCollection<IIdentityProviderPlugin> ClientProvisioningProviders { get; }

    /// <summary>
    /// Aggregate capability flags across all registered providers.
    /// </summary>
    AuthorityIdentityProviderCapabilities AggregateCapabilities { get; }

    /// <summary>
    /// Attempts to resolve an identity provider by name.
    /// </summary>
    bool TryGet(string name, [NotNullWhen(true)] out IIdentityProviderPlugin? provider);

    /// <summary>
    /// Resolves an identity provider by name or throws when not found.
    /// </summary>
    IIdentityProviderPlugin GetRequired(string name)
    {
        if (TryGet(name, out var provider))
        {
            return provider;
        }

        throw new KeyNotFoundException($"Identity provider plugin '{name}' is not registered.");
    }
}
