// Explicit usings retained; pragma used to suppress IDE0005 for clarity in reflection-heavy test
#pragma warning disable IDE0005
using System.Reflection;
#pragma warning restore IDE0005
using Sbroenne.ExcelMcp.Core.Models.Actions;
using Xunit;
using Xunit.Abstractions;

namespace Sbroenne.ExcelMcp.McpServer.Tests.Integration.Models;

/// <summary>
/// CRITICAL: Ensures all enum values have ToActionString() mappings.
/// Missing mappings cause ArgumentException at runtime when users invoke actions.
///
/// Uses reflection to automatically discover ALL action enums - no manual maintenance required.
/// </summary>
/// <inheritdoc/>
[Trait("Category", "Integration")]
[Trait("Speed", "Fast")]
[Trait("Layer", "McpServer")]
[Trait("Feature", "ActionEnums")]
[Trait("RequiresExcel", "false")]
public class ActionEnumCompletenessTests(ITestOutputHelper output)
{

    /// <summary>
    /// CRITICAL: Discovers ALL *Action enums and verifies every value has a ToActionString() mapping.
    /// This test will FAIL if:
    /// 1. New enum added without ToActionString() extension method
    /// 2. Enum value added without corresponding mapping
    /// 3. ToActionString() throws ArgumentException for any enum value
    /// </summary>
    [Fact]
    public void AllActionEnums_HaveCompleteToActionStringMappings()
    {
        // Find all *Action enums in Models namespace
        var actionEnums = typeof(ActionExtensions).Assembly
            .GetTypes()
            .Where(t => t.IsEnum && t.Name.EndsWith("Action", StringComparison.Ordinal) && t.Namespace == "Sbroenne.ExcelMcp.Core.Models.Actions")
            .ToList();

        output.WriteLine($"Found {actionEnums.Count} action enums:");
        foreach (var enumType in actionEnums)
        {
            output.WriteLine($"  - {enumType.Name}");
        }

        Assert.NotEmpty(actionEnums); // Sanity check

        var failures = new List<string>();

        foreach (var enumType in actionEnums)
        {
            // Find the ToActionString extension method for this enum
            var extensionMethod = typeof(ActionExtensions)
                .GetMethods(BindingFlags.Public | BindingFlags.Static)
                .FirstOrDefault(m =>
                    m.Name == "ToActionString" &&
                    m.GetParameters().Length == 1 &&
                    m.GetParameters()[0].ParameterType == enumType);

            if (extensionMethod == null)
            {
                failures.Add($"❌ {enumType.Name}: Missing ToActionString() extension method");
                continue;
            }

            // Get all enum values
            var enumValues = Enum.GetValues(enumType);

            foreach (var enumValue in enumValues)
            {
                try
                {
                    // Invoke ToActionString() - will throw if mapping missing
                    var result = extensionMethod.Invoke(null, [enumValue]) as string;

                    if (string.IsNullOrWhiteSpace(result))
                    {
                        failures.Add($"❌ {enumType.Name}.{enumValue}: Mapped to empty string");
                    }
                    else
                    {
                        output.WriteLine($"  ✅ {enumType.Name}.{enumValue} → '{result}'");
                    }
                }
                catch (TargetInvocationException ex) when (ex.InnerException is ArgumentException argEx)
                {
                    failures.Add($"❌ {enumType.Name}.{enumValue}: {argEx.Message}");
                }
                catch (Exception ex)
                {
                    failures.Add($"❌ {enumType.Name}.{enumValue}: Unexpected error: {ex.Message}");
                }
            }
        }

        if (failures.Count > 0)
        {
            var message = $"Enum mapping failures:\n{string.Join("\n", failures)}";
            output.WriteLine($"\n{message}");
            Assert.Fail(message);
        }
    }

    /// <summary>
    /// CRITICAL: Ensures no duplicate action strings within same enum (case-insensitive).
    /// Duplicates would cause ambiguous routing in tool switch statements.
    /// </summary>
    [Fact]
    public void AllActionEnums_NoDuplicateActionStrings()
    {
        var actionEnums = typeof(ActionExtensions).Assembly
            .GetTypes()
            .Where(t => t.IsEnum && t.Name.EndsWith("Action", StringComparison.Ordinal) && t.Namespace == "Sbroenne.ExcelMcp.Core.Models.Actions")
            .ToList();

        var failures = new List<string>();

        foreach (var enumType in actionEnums)
        {
            var extensionMethod = typeof(ActionExtensions)
                .GetMethods(BindingFlags.Public | BindingFlags.Static)
                .FirstOrDefault(m =>
                    m.Name == "ToActionString" &&
                    m.GetParameters().Length == 1 &&
                    m.GetParameters()[0].ParameterType == enumType);

            if (extensionMethod == null) continue; // Already caught by other test

            var enumValues = Enum.GetValues(enumType);
            var actionStrings = new List<(object enumValue, string actionString)>();

            foreach (var enumValue in enumValues)
            {
                try
                {
                    var result = extensionMethod.Invoke(null, [enumValue]) as string;
                    if (!string.IsNullOrWhiteSpace(result))
                    {
                        actionStrings.Add((enumValue, result.ToLowerInvariant()));
                    }
                }
                catch
                {
                    // Ignore - will be caught by completeness test
                }
            }

            var duplicates = actionStrings
                .GroupBy(x => x.actionString)
                .Where(g => g.Count() > 1)
                .ToList();

            if (duplicates.Count > 0)
            {
                foreach (var duplicate in duplicates)
                {
                    var enumValueNames = string.Join(", ", duplicate.Select(x => x.enumValue));
                    failures.Add($"❌ {enumType.Name}: Duplicate action string '{duplicate.Key}' for: {enumValueNames}");
                }
            }
        }

        if (failures.Count > 0)
        {
            var message = $"Duplicate action string failures:\n{string.Join("\n", failures)}";
            output.WriteLine($"\n{message}");
            Assert.Fail(message);
        }
    }

    /// <summary>
    /// CRITICAL: Verifies all *Tool.cs files have switch statements covering all enum values.
    /// This ensures tool implementations don't get out of sync with enums.
    ///
    /// NOTE: This is a compile-time check via exhaustive switch expressions.
    /// If switch is missing a case, C# compiler shows warning CS8524.
    /// This test documents the expectation - actual enforcement is via compiler.
    /// </summary>
    [Fact]
    public void AllActionEnums_DocumentedInToolFiles()
    {
        var actionEnums = typeof(ActionExtensions).Assembly
            .GetTypes()
            .Where(t => t.IsEnum && t.Name.EndsWith("Action", StringComparison.Ordinal) && t.Namespace == "Sbroenne.ExcelMcp.Core.Models.Actions")
            .ToList();

        output.WriteLine($"\nExpected tool files with switch statements:");
        output.WriteLine($"Each *Action enum should have corresponding *Tool.cs with exhaustive switch.\n");

        foreach (var enumType in actionEnums)
        {
            var toolName = enumType.Name.Replace("Action", "Tool");
            output.WriteLine($"  - {enumType.Name} → Tools/{toolName}.cs");
            output.WriteLine($"    Expected: switch (action.ToActionString()) with all {Enum.GetValues(enumType).Length} cases");
        }

        output.WriteLine($"\n✅ Compiler enforces exhaustive switches via warning CS8524.");
        output.WriteLine($"✅ Build with TreatWarningsAsErrors=true ensures no missing cases.");
    }
}




