using System;
using System.Linq;
using System.Reflection;
using Xunit;
using Xunit.Abstractions;

namespace ZB.MOM.WW.MxGateway.Worker.Tests;

/// <summary>
///     One-shot reflection probe — discovers the public surface of
///     <c>aaAlarmManagedClient.dll</c> so we can design the alarm-helper
///     wiring in the worker. Marked Skip so it doesn't run as part of the
///     normal suite; flip the Skip parameter to see the output.
/// </summary>
public sealed class AlarmClientDiscoveryTests
{
    private readonly ITestOutputHelper output;

    /// <summary>Initializes a new instance of the AlarmClientDiscoveryTests class.</summary>
    /// <param name="output">The xUnit test output helper.</param>
    public AlarmClientDiscoveryTests(ITestOutputHelper output)
    {
        this.output = output;
    }

    /// <summary>Dumps the public surface of the aaAlarmManagedClient assembly.</summary>
    [Fact(Skip = "Discovery probe — flip Skip=null to dump aaAlarmManagedClient surface")]
    public void DumpAlarmClientPublicSurface()
    {
        Assembly asm = Assembly.LoadFrom(@"C:\Program Files (x86)\ArchestrA\Framework\Bin\ViewAppFramework\Content\MA\aaAlarmManagedClient.dll");

        output.WriteLine($"Assembly: {asm.FullName}");
        output.WriteLine("Public types:");

        Type[] types = asm.GetExportedTypes()
            .OrderBy(t => t.FullName, StringComparer.Ordinal)
            .ToArray();
        foreach (Type t in types)
        {
            output.WriteLine($"  {t.FullName} ({(t.IsClass ? "class" : t.IsInterface ? "interface" : t.IsEnum ? "enum" : "other")})");
        }

        output.WriteLine("");
        output.WriteLine("Public events / methods on alarm-named types:");
        foreach (Type t in types.Where(x => x.Name.IndexOf("Alarm", StringComparison.OrdinalIgnoreCase) >= 0
                                           || x.Name.IndexOf("Subscription", StringComparison.OrdinalIgnoreCase) >= 0
                                           || x.Name.IndexOf("Event", StringComparison.OrdinalIgnoreCase) >= 0))
        {
            output.WriteLine($"  {t.FullName}");
            foreach (EventInfo e in t.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
            {
                output.WriteLine($"    event {e.EventHandlerType?.Name} {e.Name}");
            }
            foreach (MethodInfo m in t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            {
                if (m.IsSpecialName) continue;
                string parms = string.Join(", ", m.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
                output.WriteLine($"    method {m.ReturnType.Name} {m.Name}({parms})");
            }
        }

        // Probe AlarmRecord + enum types reachable from AlarmClient's module —
        // these are typically internal to the assembly but referenced by the
        // public API methods we just dumped.
        output.WriteLine("");
        output.WriteLine("Reachable types in the AlarmClient module:");
        Type alarmClient = asm.GetType("aaAlarmManagedClient.AlarmClient")!;
        foreach (Type t in alarmClient.Module.GetTypes()
            .Where(t => !t.IsNested)
            .OrderBy(t => t.FullName, StringComparer.Ordinal))
        {
            string visibility = t.IsPublic ? "public" : "internal";
            string kind = t.IsEnum ? "enum" : t.IsValueType ? "struct" : t.IsInterface ? "interface" : "class";
            output.WriteLine($"  {visibility} {kind} {t.FullName}");
            if (t.IsEnum)
            {
                foreach (string n in Enum.GetNames(t))
                {
                    object val = Enum.Parse(t, n);
                    output.WriteLine($"    {n} = {Convert.ToInt64(val)}");
                }
            }
            else if (t.Name.IndexOf("AlarmRecord", StringComparison.OrdinalIgnoreCase) >= 0
                     || t.Name.IndexOf("Selected", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                foreach (FieldInfo f in t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
                {
                    output.WriteLine($"    field {f.FieldType.Name} {f.Name}");
                }
                foreach (PropertyInfo p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    output.WriteLine($"    prop {p.PropertyType.Name} {p.Name}");
                }
            }
        }
    }
}
