using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using StellaOps.Authority.Plugins.Abstractions;
using StellaOps.Configuration;

namespace StellaOps.Cli.Services;

/// <summary>
/// Emits Authority configuration diagnostics discovered during CLI startup.
/// </summary>
internal static class AuthorityDiagnosticsReporter
{
    public static void Emit(IConfiguration configuration, ILogger logger)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        ArgumentNullException.ThrowIfNull(logger);

        var basePath = Directory.GetCurrentDirectory();
        EmitInternal(configuration, logger, basePath);
    }

    internal static void Emit(IConfiguration configuration, ILogger logger, string basePath)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        ArgumentNullException.ThrowIfNull(logger);
        ArgumentNullException.ThrowIfNull(basePath);

        EmitInternal(configuration, logger, basePath);
    }

    private static void EmitInternal(IConfiguration configuration, ILogger logger, string basePath)
    {
        if (string.IsNullOrWhiteSpace(basePath))
        {
            basePath = Directory.GetCurrentDirectory();
        }

        var authoritySection = configuration.GetSection("Authority");
        if (!authoritySection.Exists())
        {
            return;
        }

        var authorityOptions = new StellaOpsAuthorityOptions();
        authoritySection.Bind(authorityOptions);

        if (authorityOptions.Plugins.Descriptors.Count == 0)
        {
            return;
        }

        var resolvedBasePath = Path.GetFullPath(basePath);
        IReadOnlyList<AuthorityPluginContext> contexts;

        try
        {
            contexts = AuthorityPluginConfigurationLoader.Load(authorityOptions, resolvedBasePath);
        }
        catch (Exception ex)
        {
            logger.LogDebug(ex, "Failed to load Authority plug-in configuration for diagnostics.");
            return;
        }

        if (contexts.Count == 0)
        {
            return;
        }

        IReadOnlyList<AuthorityConfigurationDiagnostic> diagnostics;
        try
        {
            diagnostics = AuthorityPluginConfigurationAnalyzer.Analyze(contexts);
        }
        catch (Exception ex)
        {
            logger.LogDebug(ex, "Failed to analyze Authority plug-in configuration for diagnostics.");
            return;
        }

        if (diagnostics.Count == 0)
        {
            return;
        }

        var contextLookup = new Dictionary<string, AuthorityPluginContext>(StringComparer.OrdinalIgnoreCase);
        foreach (var context in contexts)
        {
            if (context?.Manifest?.Name is { Length: > 0 } name && !contextLookup.ContainsKey(name))
            {
                contextLookup[name] = context;
            }
        }

        foreach (var diagnostic in diagnostics)
        {
            var level = diagnostic.Severity switch
            {
                AuthorityConfigurationDiagnosticSeverity.Error => LogLevel.Error,
                AuthorityConfigurationDiagnosticSeverity.Warning => LogLevel.Warning,
                _ => LogLevel.Information
            };

            if (!logger.IsEnabled(level))
            {
                continue;
            }

            if (contextLookup.TryGetValue(diagnostic.PluginName, out var context) &&
                context?.Manifest?.ConfigPath is { Length: > 0 } configPath)
            {
                logger.Log(level, "{DiagnosticMessage} (config: {ConfigPath})", diagnostic.Message, configPath);
            }
            else
            {
                logger.Log(level, "{DiagnosticMessage}", diagnostic.Message);
            }
        }
    }
}
