using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Configuration;
using StellaOps.Authority.Plugins.Abstractions;

namespace StellaOps.Configuration;

/// <summary>
/// Analyses Authority plugin configurations for common security issues.
/// </summary>
public static class AuthorityPluginConfigurationAnalyzer
{
    private const int BaselineMinimumLength = 12;
    private const bool BaselineRequireUppercase = true;
    private const bool BaselineRequireLowercase = true;
    private const bool BaselineRequireDigit = true;
    private const bool BaselineRequireSymbol = true;

    /// <summary>
    /// Evaluates plugin contexts and returns diagnostics describing potential misconfigurations.
    /// </summary>
    /// <param name="contexts">Plugin contexts produced by <see cref="AuthorityPluginConfigurationLoader"/>.</param>
    /// <returns>Diagnostics describing any detected issues.</returns>
    public static IReadOnlyList<AuthorityConfigurationDiagnostic> Analyze(IEnumerable<AuthorityPluginContext> contexts)
    {
        ArgumentNullException.ThrowIfNull(contexts);

        var diagnostics = new List<AuthorityConfigurationDiagnostic>();

        foreach (var context in contexts)
        {
            if (context is null)
            {
                continue;
            }

            if (string.Equals(context.Manifest.AssemblyName, "StellaOps.Authority.Plugin.Standard", StringComparison.OrdinalIgnoreCase))
            {
                AnalyzeStandardPlugin(context, diagnostics);
            }
        }

        return diagnostics;
    }

    private static void AnalyzeStandardPlugin(AuthorityPluginContext context, ICollection<AuthorityConfigurationDiagnostic> diagnostics)
    {
        var section = context.Configuration.GetSection("passwordPolicy");
        if (!section.Exists())
        {
            return;
        }

        int minLength = section.GetValue("minimumLength", BaselineMinimumLength);
        bool requireUppercase = section.GetValue("requireUppercase", BaselineRequireUppercase);
        bool requireLowercase = section.GetValue("requireLowercase", BaselineRequireLowercase);
        bool requireDigit = section.GetValue("requireDigit", BaselineRequireDigit);
        bool requireSymbol = section.GetValue("requireSymbol", BaselineRequireSymbol);

        var deviations = new List<string>();

        if (minLength < BaselineMinimumLength)
        {
            deviations.Add($"minimum length {minLength.ToString(CultureInfo.InvariantCulture)} < {BaselineMinimumLength}");
        }

        if (!requireUppercase && BaselineRequireUppercase)
        {
            deviations.Add("uppercase requirement disabled");
        }

        if (!requireLowercase && BaselineRequireLowercase)
        {
            deviations.Add("lowercase requirement disabled");
        }

        if (!requireDigit && BaselineRequireDigit)
        {
            deviations.Add("digit requirement disabled");
        }

        if (!requireSymbol && BaselineRequireSymbol)
        {
            deviations.Add("symbol requirement disabled");
        }

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

        var message = $"Password policy for plugin '{context.Manifest.Name}' weakens host defaults: {string.Join(", ", deviations)}.";
        diagnostics.Add(new AuthorityConfigurationDiagnostic(context.Manifest.Name, AuthorityConfigurationDiagnosticSeverity.Warning, message));
    }
}
