using System.Reflection;
using GFramework.Cqrs.SourceGenerators.Cqrs;
using GFramework.SourceGenerators.Tests.Core;

namespace GFramework.SourceGenerators.Tests.Cqrs;

/// <summary>
///     验证 CQRS 处理器注册生成器的输出与回退边界。
/// </summary>
[TestFixture]
public class CqrsHandlerRegistryGeneratorTests
{
    private const string HiddenNestedHandlerSelfRegistrationExpected = """
                                                                       // <auto-generated />
                                                                       #nullable enable

                                                                       [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

                                                                       namespace GFramework.Generated.Cqrs;

                                                                       internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
                                                                       {
                                                                           public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
                                                                           {
                                                                               if (services is null)
                                                                                   throw new global::System.ArgumentNullException(nameof(services));
                                                                               if (logger is null)
                                                                                   throw new global::System.ArgumentNullException(nameof(logger));

                                                                               var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                                                                               var implementationType0 = registryAssembly.GetType("TestApp.Container+HiddenHandler", throwOnError: false, ignoreCase: false);
                                                                               if (implementationType0 is not null)
                                                                               {
                                                                                   var serviceType0_0Argument0 = registryAssembly.GetType("TestApp.Container+HiddenRequest", throwOnError: false, ignoreCase: false);
                                                                                   if (serviceType0_0Argument0 is not null)
                                                                                   {
                                                                                       var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, typeof(string));
                                                                                       global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                           services,
                                                                                           serviceType0_0,
                                                                                           implementationType0);
                                                                                       logger.Debug("Registered CQRS handler TestApp.Container.HiddenHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.Container.HiddenRequest, string>.");
                                                                                   }
                                                                               }
                                                                               global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                   services,
                                                                                   typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::TestApp.VisibleRequest, string>),
                                                                                   typeof(global::TestApp.VisibleHandler));
                                                                               logger.Debug("Registered CQRS handler TestApp.VisibleHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.VisibleRequest, string>.");
                                                                           }
                                                                       }

                                                                       """;

    private const string HiddenImplementationDirectInterfaceRegistrationExpected = """
        // <auto-generated />
        #nullable enable

        [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

        namespace GFramework.Generated.Cqrs;

        internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
        {
            public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
            {
                if (services is null)
                    throw new global::System.ArgumentNullException(nameof(services));
                if (logger is null)
                    throw new global::System.ArgumentNullException(nameof(logger));

                var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                var implementationType0 = registryAssembly.GetType("TestApp.Container+HiddenHandler", throwOnError: false, ignoreCase: false);
                if (implementationType0 is not null)
                {
                    global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                        services,
                        typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::TestApp.VisibleRequest, string>),
                        implementationType0);
                    logger.Debug("Registered CQRS handler TestApp.Container.HiddenHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.VisibleRequest, string>.");
                }
            }
        }

        """;

    private const string HiddenArrayResponseFallbackExpected = """
                                                               // <auto-generated />
                                                               #nullable enable

                                                               [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

                                                               namespace GFramework.Generated.Cqrs;

                                                               internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
                                                               {
                                                                   public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
                                                                   {
                                                                       if (services is null)
                                                                           throw new global::System.ArgumentNullException(nameof(services));
                                                                       if (logger is null)
                                                                           throw new global::System.ArgumentNullException(nameof(logger));

                                                                       var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                                                                       var implementationType0 = registryAssembly.GetType("TestApp.Container+HiddenHandler", throwOnError: false, ignoreCase: false);
                                                                       if (implementationType0 is not null)
                                                                       {
                                                                           var serviceType0_0Argument0 = registryAssembly.GetType("TestApp.Container+HiddenRequest", throwOnError: false, ignoreCase: false);
                                                                           var serviceType0_0Argument1Element = registryAssembly.GetType("TestApp.Container+HiddenResponse", throwOnError: false, ignoreCase: false);
                                                                           if (serviceType0_0Argument0 is not null && serviceType0_0Argument1Element is not null)
                                                                           {
                                                                               var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1Element.MakeArrayType());
                                                                               global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                   services,
                                                                                   serviceType0_0,
                                                                                   implementationType0);
                                                                               logger.Debug("Registered CQRS handler TestApp.Container.HiddenHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.Container.HiddenRequest, TestApp.Container.HiddenResponse[]>.");
                                                                           }
                                                                       }
                                                                   }
                                                               }

                                                               """;

    private const string HiddenGenericEnvelopeResponseExpected = """
                                                                 // <auto-generated />
                                                                 #nullable enable

                                                                 [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

                                                                 namespace GFramework.Generated.Cqrs;

                                                                 internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
                                                                 {
                                                                     public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
                                                                     {
                                                                         if (services is null)
                                                                             throw new global::System.ArgumentNullException(nameof(services));
                                                                         if (logger is null)
                                                                             throw new global::System.ArgumentNullException(nameof(logger));

                                                                         var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                                                                         var implementationType0 = registryAssembly.GetType("TestApp.Container+HiddenHandler", throwOnError: false, ignoreCase: false);
                                                                         if (implementationType0 is not null)
                                                                         {
                                                                             var serviceType0_0Argument0 = registryAssembly.GetType("TestApp.Container+HiddenRequest", throwOnError: false, ignoreCase: false);
                                                                             var serviceType0_0Argument1GenericDefinition = registryAssembly.GetType("TestApp.Container+HiddenEnvelope`1", throwOnError: false, ignoreCase: false);
                                                                             if (serviceType0_0Argument0 is not null && serviceType0_0Argument1GenericDefinition is not null)
                                                                             {
                                                                                 var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1GenericDefinition.MakeGenericType(typeof(string)));
                                                                                 global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                     services,
                                                                                     serviceType0_0,
                                                                                     implementationType0);
                                                                                 logger.Debug("Registered CQRS handler TestApp.Container.HiddenHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.Container.HiddenRequest, TestApp.Container.HiddenEnvelope<string>>.");
                                                                             }
                                                                         }
                                                                     }
                                                                 }

                                                                 """;

    private const string MixedDirectAndPreciseRegistrationsExpected = """
                                                                      // <auto-generated />
                                                                      #nullable enable

                                                                      [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

                                                                      namespace GFramework.Generated.Cqrs;

                                                                      internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
                                                                      {
                                                                          public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
                                                                          {
                                                                              if (services is null)
                                                                                  throw new global::System.ArgumentNullException(nameof(services));
                                                                              if (logger is null)
                                                                                  throw new global::System.ArgumentNullException(nameof(logger));

                                                                              var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                                                                              var implementationType0 = typeof(global::TestApp.Container.MixedHandler);
                                                                              if (implementationType0 is not null)
                                                                              {
                                                                                  var serviceType0_0Argument0 = registryAssembly.GetType("TestApp.Container+HiddenRequest", throwOnError: false, ignoreCase: false);
                                                                                  var serviceType0_0Argument1Element = registryAssembly.GetType("TestApp.Container+HiddenResponse", throwOnError: false, ignoreCase: false);
                                                                                  if (serviceType0_0Argument0 is not null && serviceType0_0Argument1Element is not null)
                                                                                  {
                                                                                      var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1Element.MakeArrayType());
                                                                                      global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                          services,
                                                                                          serviceType0_0,
                                                                                          implementationType0);
                                                                                      logger.Debug("Registered CQRS handler TestApp.Container.MixedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.Container.HiddenRequest, TestApp.Container.HiddenResponse[]>.");
                                                                                  }
                                                                                  global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                      services,
                                                                                      typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::TestApp.VisibleRequest, string>),
                                                                                      implementationType0);
                                                                                  logger.Debug("Registered CQRS handler TestApp.Container.MixedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.VisibleRequest, string>.");
                                                                              }
                                                                          }
                                                                      }

                                                                      """;

    private const string MixedReflectedImplementationAndPreciseRegistrationsExpected = """
        // <auto-generated />
        #nullable enable

        [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

        namespace GFramework.Generated.Cqrs;

        internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
        {
            public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
            {
                if (services is null)
                    throw new global::System.ArgumentNullException(nameof(services));
                if (logger is null)
                    throw new global::System.ArgumentNullException(nameof(logger));

                var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                var implementationType0 = registryAssembly.GetType("TestApp.Container+HiddenMixedHandler", throwOnError: false, ignoreCase: false);
                if (implementationType0 is not null)
                {
                    var serviceType0_0Argument0 = registryAssembly.GetType("TestApp.Container+HiddenRequest", throwOnError: false, ignoreCase: false);
                    var serviceType0_0Argument1Element = registryAssembly.GetType("TestApp.Container+HiddenResponse", throwOnError: false, ignoreCase: false);
                    if (serviceType0_0Argument0 is not null && serviceType0_0Argument1Element is not null)
                    {
                        var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1Element.MakeArrayType());
                        global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                            services,
                            serviceType0_0,
                            implementationType0);
                        logger.Debug("Registered CQRS handler TestApp.Container.HiddenMixedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.Container.HiddenRequest, TestApp.Container.HiddenResponse[]>.");
                    }
                    global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                        services,
                        typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::TestApp.VisibleRequest, string>),
                        implementationType0);
                    logger.Debug("Registered CQRS handler TestApp.Container.HiddenMixedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.VisibleRequest, string>.");
                }
            }
        }

        """;

    private const string ExternalAssemblyPreciseLookupExpected = """
                                                                 // <auto-generated />
                                                                 #nullable enable

                                                                 [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

                                                                 namespace GFramework.Generated.Cqrs;

                                                                 internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
                                                                 {
                                                                     public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
                                                                     {
                                                                         if (services is null)
                                                                             throw new global::System.ArgumentNullException(nameof(services));
                                                                         if (logger is null)
                                                                             throw new global::System.ArgumentNullException(nameof(logger));

                                                                         var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;

                                                                         var implementationType0 = typeof(global::TestApp.DerivedHandler);
                                                                         if (implementationType0 is not null)
                                                                         {
                                                                             var serviceType0_0Argument0 = ResolveReferencedAssemblyType("Dependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "Dep.VisibilityScope+ProtectedRequest");
                                                                             var serviceType0_0Argument1Element = ResolveReferencedAssemblyType("Dependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "Dep.VisibilityScope+ProtectedResponse");
                                                                             if (serviceType0_0Argument0 is not null && serviceType0_0Argument1Element is not null)
                                                                             {
                                                                                 var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1Element.MakeArrayType());
                                                                                 global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                     services,
                                                                                     serviceType0_0,
                                                                                     implementationType0);
                                                                                 logger.Debug("Registered CQRS handler TestApp.DerivedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>.");
                                                                             }
                                                                             global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                                                                 services,
                                                                                 typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibleRequest, string>),
                                                                                 implementationType0);
                                                                             logger.Debug("Registered CQRS handler TestApp.DerivedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibleRequest, string>.");
                                                                         }
                                                                     }

                                                                     private static global::System.Type? ResolveReferencedAssemblyType(string assemblyIdentity, string typeMetadataName)
                                                                     {
                                                                         var assembly = ResolveReferencedAssembly(assemblyIdentity);
                                                                         return assembly?.GetType(typeMetadataName, throwOnError: false, ignoreCase: false);
                                                                     }

                                                                     private static global::System.Reflection.Assembly? ResolveReferencedAssembly(string assemblyIdentity)
                                                                     {
                                                                         global::System.Reflection.AssemblyName targetAssemblyName;
                                                                         try
                                                                         {
                                                                             targetAssemblyName = new global::System.Reflection.AssemblyName(assemblyIdentity);
                                                                         }
                                                                         catch
                                                                         {
                                                                             return null;
                                                                         }

                                                                         foreach (var assembly in global::System.AppDomain.CurrentDomain.GetAssemblies())
                                                                         {
                                                                             if (global::System.Reflection.AssemblyName.ReferenceMatchesDefinition(targetAssemblyName, assembly.GetName()))
                                                                                 return assembly;
                                                                         }

                                                                         try
                                                                         {
                                                                             return global::System.Reflection.Assembly.Load(targetAssemblyName);
                                                                         }
                                                                         catch
                                                                         {
                                                                             return null;
                                                                         }
                                                                     }
                                                                 }

                                                                 """;

    /// <summary>
    ///     验证生成器会为当前程序集中的 request、notification 和 stream 处理器生成稳定顺序的注册器。
    /// </summary>
    [Test]
    public async Task Generates_Assembly_Level_Cqrs_Handler_Registry()
    {
        const string source = """
                              using System;
                              using System.Collections.Generic;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly)]
                                  public sealed class CqrsReflectionFallbackAttribute : Attribute
                                  {
                                      public CqrsReflectionFallbackAttribute(params string[] fallbackHandlerTypeNames) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record PingQuery() : IRequest<string>;
                                  public sealed record DomainEvent() : INotification;
                                  public sealed record NumberStream() : IStreamRequest<int>;

                                  public sealed class ZetaNotificationHandler : INotificationHandler<DomainEvent> { }
                                  public sealed class AlphaQueryHandler : IRequestHandler<PingQuery, string> { }
                                  public sealed class StreamHandler : IStreamRequestHandler<NumberStream, int> { }
                              }
                              """;

        const string expected = """
                                // <auto-generated />
                                #nullable enable

                                [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]

                                namespace GFramework.Generated.Cqrs;

                                internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
                                {
                                    public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
                                    {
                                        if (services is null)
                                            throw new global::System.ArgumentNullException(nameof(services));
                                        if (logger is null)
                                            throw new global::System.ArgumentNullException(nameof(logger));

                                        global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                            services,
                                            typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::TestApp.PingQuery, string>),
                                            typeof(global::TestApp.AlphaQueryHandler));
                                        logger.Debug("Registered CQRS handler TestApp.AlphaQueryHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.PingQuery, string>.");
                                        global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                            services,
                                            typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler<global::TestApp.NumberStream, int>),
                                            typeof(global::TestApp.StreamHandler));
                                        logger.Debug("Registered CQRS handler TestApp.StreamHandler as GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler<TestApp.NumberStream, int>.");
                                        global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
                                            services,
                                            typeof(global::GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler<global::TestApp.DomainEvent>),
                                            typeof(global::TestApp.ZetaNotificationHandler));
                                        logger.Debug("Registered CQRS handler TestApp.ZetaNotificationHandler as GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler<TestApp.DomainEvent>.");
                                    }
                                }

                                """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", expected));
    }

    /// <summary>
    ///     验证当程序集包含生成代码无法合法引用的私有嵌套处理器时，生成器会在生成注册器内部执行定向反射注册，
    ///     不再依赖程序集级 fallback marker。
    /// </summary>
    [Test]
    public async Task
        Generates_Visible_Handlers_And_Self_Registers_Private_Nested_Handler_When_Assembly_Contains_Hidden_Handler()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly)]
                                  public sealed class CqrsReflectionFallbackAttribute : Attribute
                                  {
                                      public CqrsReflectionFallbackAttribute(params string[] fallbackHandlerTypeNames) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record VisibleRequest() : IRequest<string>;

                                  public sealed class Container
                                  {
                                      private sealed record HiddenRequest() : IRequest<string>;

                                      private sealed class HiddenHandler : IRequestHandler<HiddenRequest, string> { }
                                  }

                                  public sealed class VisibleHandler : IRequestHandler<VisibleRequest, string> { }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", HiddenNestedHandlerSelfRegistrationExpected));
    }

    /// <summary>
    ///     验证当隐藏实现类型的 handler 接口仍可被生成代码直接引用时，
    ///     生成器只会定向反射实现类型，而不会再生成基于 <c>GetInterfaces()</c> 的接口发现辅助逻辑。
    /// </summary>
    [Test]
    public async Task
        Generates_Direct_Interface_Registrations_For_Hidden_Implementation_When_Handler_Interface_Is_Public()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record VisibleRequest() : IRequest<string>;

                                  public sealed class Container
                                  {
                                      private sealed class HiddenHandler : IRequestHandler<VisibleRequest, string> { }
                                  }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", HiddenImplementationDirectInterfaceRegistrationExpected));
    }

    /// <summary>
    ///     验证精确重建路径会递归覆盖隐藏元素类型数组，
    ///     使这类 handler interface 也能直接生成 closed service type，而不再退回 <c>GetInterfaces()</c>。
    /// </summary>
    [Test]
    public async Task Generates_Precise_Service_Type_For_Hidden_Array_Type_Arguments()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed class Container
                                  {
                                      private sealed record HiddenResponse();

                                      private sealed record HiddenRequest() : IRequest<HiddenResponse[]>;

                                      private sealed class HiddenHandler : IRequestHandler<HiddenRequest, HiddenResponse[]> { }
                                  }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", HiddenArrayResponseFallbackExpected));
    }

    /// <summary>
    ///     验证精确重建路径会递归覆盖隐藏泛型定义，
    ///     使“隐藏泛型定义 + 可见/常量型实参”的闭包类型也能直接生成 closed service type。
    /// </summary>
    [Test]
    public async Task Generates_Precise_Service_Type_For_Hidden_Generic_Type_Definitions()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed class Container
                                  {
                                      private sealed class HiddenEnvelope<T> { }

                                      private sealed record HiddenRequest() : IRequest<HiddenEnvelope<string>>;

                                      private sealed class HiddenHandler : IRequestHandler<HiddenRequest, HiddenEnvelope<string>> { }
                                  }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", HiddenGenericEnvelopeResponseExpected));
    }

    /// <summary>
    ///     验证同一个 implementation 同时包含可直接注册接口与需精确重建接口时，
    ///     生成器会保留两类注册，并继续按 handler interface 名称稳定排序。
    /// </summary>
    [Test]
    public async Task Generates_Mixed_Direct_And_Precise_Registrations_For_Same_Implementation()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record VisibleRequest() : IRequest<string>;

                                  public sealed class Container
                                  {
                                      private sealed record HiddenResponse();

                                      private sealed record HiddenRequest() : IRequest<HiddenResponse[]>;

                                      public sealed class MixedHandler :
                                          IRequestHandler<HiddenRequest, HiddenResponse[]>,
                                          IRequestHandler<VisibleRequest, string>
                                      {
                                      }
                                  }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", MixedDirectAndPreciseRegistrationsExpected));
    }

    /// <summary>
    ///     验证隐藏 implementation 同时包含可见 handler interface 与需精确重建接口时，
    ///     生成器会保留两类注册，而不会让可见接口被整实现回退吞掉。
    /// </summary>
    [Test]
    public async Task Generates_Mixed_Reflected_Implementation_And_Precise_Registrations_For_Same_Implementation()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record VisibleRequest() : IRequest<string>;

                                  public sealed class Container
                                  {
                                      private sealed record HiddenResponse();

                                      private sealed record HiddenRequest() : IRequest<HiddenResponse[]>;

                                      private sealed class HiddenMixedHandler :
                                          IRequestHandler<HiddenRequest, HiddenResponse[]>,
                                          IRequestHandler<VisibleRequest, string>
                                      {
                                      }
                                  }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", MixedReflectedImplementationAndPreciseRegistrationsExpected));
    }

    /// <summary>
    ///     验证当外部基类暴露的 handler interface 含有生成注册器顶层上下文不可直接引用的 protected 类型时，
    ///     生成器会输出定向程序集查找，而不是继续退回 implementation 级接口发现。
    /// </summary>
    [Test]
    public void Generates_Precise_Assembly_Type_Lookups_For_Inaccessible_External_Protected_Types()
    {
        const string contractsSource = """
                                       namespace GFramework.Cqrs.Abstractions.Cqrs
                                       {
                                           public interface IRequest<TResponse> { }
                                           public interface INotification { }
                                           public interface IStreamRequest<TResponse> { }

                                           public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                           public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                           public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                                       }
                                       """;

        const string dependencySource = """
                                        using GFramework.Cqrs.Abstractions.Cqrs;

                                        namespace Dep;

                                        public sealed record VisibleRequest() : IRequest<string>;

                                        public abstract class VisibilityScope
                                        {
                                            protected internal sealed record ProtectedResponse();

                                            protected internal sealed record ProtectedRequest() : IRequest<ProtectedResponse[]>;
                                        }

                                        public abstract class HandlerBase :
                                            VisibilityScope,
                                            IRequestHandler<VisibleRequest, string>,
                                            IRequestHandler<VisibilityScope.ProtectedRequest, VisibilityScope.ProtectedResponse[]>
                                        {
                                        }
                                        """;

        const string source = """
                              using System;
                              using Dep;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  public sealed class DerivedHandler : HandlerBase
                                  {
                                  }
                              }
                              """;

        var contractsReference = MetadataReferenceTestBuilder.CreateFromSource(
            "Contracts",
            contractsSource);
        var dependencyReference = MetadataReferenceTestBuilder.CreateFromSource(
            "Dependency",
            dependencySource,
            contractsReference);
        var generatedSource = RunGenerator(
            source,
            contractsReference,
            dependencyReference);

        Assert.That(
            generatedSource,
            Does.Not.Contain("RegisterRemainingReflectedHandlerInterfaces("));
        Assert.That(
            generatedSource,
            Does.Not.Contain("Remaining runtime interface discovery target:"));
        Assert.That(
            generatedSource,
            Is.EqualTo(ExternalAssemblyPreciseLookupExpected));
    }

    /// <summary>
    ///     验证即使 runtime 仍暴露旧版无参 fallback marker，生成器也会优先在生成注册器内部处理隐藏 handler，
    ///     不再输出 fallback marker。
    /// </summary>
    [Test]
    public async Task Does_Not_Emit_Legacy_Fallback_Marker_When_Generated_Registry_Can_Self_Register_Hidden_Handler()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly)]
                                  public sealed class CqrsReflectionFallbackAttribute : Attribute
                                  {
                                      public CqrsReflectionFallbackAttribute() { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record VisibleRequest() : IRequest<string>;

                                  public sealed class Container
                                  {
                                      private sealed record HiddenRequest() : IRequest<string>;

                                      private sealed class HiddenHandler : IRequestHandler<HiddenRequest, string> { }
                                  }

                                  public sealed class VisibleHandler : IRequestHandler<VisibleRequest, string> { }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", HiddenNestedHandlerSelfRegistrationExpected));
    }

    /// <summary>
    ///     验证即使 runtime 合同中完全不存在 reflection fallback 标记特性，
    ///     生成器仍能通过生成注册器内部的定向反射逻辑覆盖隐藏 handler。
    /// </summary>
    [Test]
    public async Task Generates_Registry_For_Hidden_Handler_When_Fallback_Marker_Is_Unavailable()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed record VisibleRequest() : IRequest<string>;

                                  public sealed class Container
                                  {
                                      private sealed record HiddenRequest() : IRequest<string>;

                                      private sealed class HiddenHandler : IRequestHandler<HiddenRequest, string> { }
                                  }

                                  public sealed class VisibleHandler : IRequestHandler<VisibleRequest, string> { }
                              }
                              """;

        await GeneratorTest<CqrsHandlerRegistryGenerator>.RunAsync(
            source,
            ("CqrsHandlerRegistry.g.cs", HiddenNestedHandlerSelfRegistrationExpected));
    }

    /// <summary>
    ///     验证当某轮生成仍然需要程序集级 reflection fallback 元数据，且 runtime 合同缺少承载该元数据的特性时，
    ///     生成器会给出明确诊断并停止输出注册器。
    /// </summary>
    [Test]
    public void
        Reports_Diagnostic_And_Skips_Registry_When_Fallback_Metadata_Is_Required_But_Runtime_Contract_Lacks_Fallback_Attribute()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed class Container
                                  {
                                      private unsafe struct HiddenResponse
                                      {
                                      }

                                      private unsafe sealed record HiddenRequest() : IRequest<HiddenResponse*>;

                                      public unsafe sealed class HiddenHandler : IRequestHandler<HiddenRequest, HiddenResponse*>
                                      {
                                      }
                                  }
                              }
                              """;

        var execution = ExecuteGenerator(
            source,
            allowUnsafe: true);
        var generatedCompilationErrors = execution.GeneratedCompilationDiagnostics
            .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
            .ToArray();
        var generatorErrors = execution.GeneratorDiagnostics
            .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
            .ToArray();
        var missingContractDiagnostic =
            generatorErrors.SingleOrDefault(static diagnostic => diagnostic.Id == "GF_Cqrs_001");

        Assert.Multiple(() =>
        {
            Assert.That(generatedCompilationErrors, Is.Empty);
            Assert.That(execution.GeneratedSources, Is.Empty);
            Assert.That(missingContractDiagnostic, Is.Not.Null);
            Assert.That(
                missingContractDiagnostic!.GetMessage(),
                Does.Contain("TestApp.Container+HiddenHandler"));
            Assert.That(
                missingContractDiagnostic.GetMessage(),
                Does.Contain("GFramework.Cqrs.CqrsReflectionFallbackAttribute"));
        });
    }

    /// <summary>
    ///     验证当 fallback metadata 仍然必需且 runtime 提供了承载契约时，
    ///     生成器会继续产出注册器并发射程序集级 <c>CqrsReflectionFallbackAttribute</c>。
    /// </summary>
    [Test]
    public void
        Emits_Assembly_Level_Fallback_Metadata_When_Fallback_Is_Required_And_Runtime_Contract_Is_Available()
    {
        const string source = """
                              using System;

                              namespace Microsoft.Extensions.DependencyInjection
                              {
                                  public interface IServiceCollection { }

                                  public static class ServiceCollectionServiceExtensions
                                  {
                                      public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
                                  }
                              }

                              namespace GFramework.Core.Abstractions.Logging
                              {
                                  public interface ILogger
                                  {
                                      void Debug(string msg);
                                  }
                              }

                              namespace GFramework.Cqrs.Abstractions.Cqrs
                              {
                                  public interface IRequest<TResponse> { }
                                  public interface INotification { }
                                  public interface IStreamRequest<TResponse> { }

                                  public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
                                  public interface INotificationHandler<in TNotification> where TNotification : INotification { }
                                  public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
                              }

                              namespace GFramework.Cqrs
                              {
                                  public interface ICqrsHandlerRegistry
                                  {
                                      void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
                                  public sealed class CqrsHandlerRegistryAttribute : Attribute
                                  {
                                      public CqrsHandlerRegistryAttribute(Type registryType) { }
                                  }

                                  [AttributeUsage(AttributeTargets.Assembly)]
                                  public sealed class CqrsReflectionFallbackAttribute : Attribute
                                  {
                                      public CqrsReflectionFallbackAttribute(params string[] fallbackHandlerTypeNames) { }
                                  }
                              }

                              namespace TestApp
                              {
                                  using GFramework.Cqrs.Abstractions.Cqrs;

                                  public sealed class Container
                                  {
                                      private unsafe struct AlphaResponse
                                      {
                                      }

                                      private unsafe struct BetaResponse
                                      {
                                      }

                                      private unsafe sealed record AlphaRequest() : IRequest<AlphaResponse*>;

                                      private unsafe sealed record BetaRequest() : IRequest<BetaResponse*>;

                                      public unsafe sealed class BetaHandler : IRequestHandler<BetaRequest, BetaResponse*>
                                      {
                                      }

                                      public unsafe sealed class AlphaHandler : IRequestHandler<AlphaRequest, AlphaResponse*>
                                      {
                                      }
                                  }
                              }
                              """;

        var execution = ExecuteGenerator(
            source,
            allowUnsafe: true);
        var generatedCompilationErrors = execution.GeneratedCompilationDiagnostics
            .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
            .ToArray();
        var generatorErrors = execution.GeneratorDiagnostics
            .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
            .ToArray();

        Assert.Multiple(() =>
        {
            Assert.That(generatedCompilationErrors, Is.Empty);
            Assert.That(generatorErrors, Is.Empty);
            Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
            Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
            Assert.That(
                execution.GeneratedSources[0].content,
                Does.Contain(
                    "[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\"TestApp.Container+AlphaHandler\", \"TestApp.Container+BetaHandler\")]"));
            Assert.That(
                execution.GeneratedSources[0].content,
                Does.Contain(
                    "[assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]"));
            Assert.That(
                execution.GeneratedSources[0].content,
                Does.Contain("internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry"));
        });
    }

    /// <summary>
    ///     验证日志字符串转义会覆盖换行、反斜杠和双引号，避免生成代码中的字符串字面量被意外截断。
    /// </summary>
    [Test]
    public void Escape_String_Literal_Handles_Control_Characters()
    {
        var method = typeof(CqrsHandlerRegistryGenerator).GetMethod(
            "EscapeStringLiteral",
            BindingFlags.NonPublic | BindingFlags.Static);

        Assert.That(method, Is.Not.Null);

        const string input = "line1\r\nline2\\\"";
        const string expected = "line1\\r\\nline2\\\\\\\"";
        var escaped = method!.Invoke(null, [input]) as string;

        Assert.That(escaped, Is.EqualTo(expected));
    }

    /// <summary>
    ///     运行 CQRS handler registry generator，并返回单个生成文件的源码文本。
    /// </summary>
    private static string RunGenerator(
        string source,
        params MetadataReference[] additionalReferences)
    {
        var execution = ExecuteGenerator(
            source,
            allowUnsafe: false,
            additionalReferences: additionalReferences);
        var generatorErrors = execution.GeneratorDiagnostics
            .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
            .ToArray();
        Assert.That(
            generatorErrors,
            Is.Empty,
            () =>
                $"执行生成器时出现错误:{Environment.NewLine}{string.Join(Environment.NewLine, generatorErrors.Select(static diagnostic => diagnostic.ToString()))}");
        var compilationErrors = execution.CompilationDiagnostics
            .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
            .ToArray();
        Assert.That(
            compilationErrors,
            Is.Empty,
            () =>
                $"编译生成的代码时出现错误:{Environment.NewLine}{string.Join(Environment.NewLine, compilationErrors.Select(static diagnostic => diagnostic.ToString()))}");
        Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));

        return execution.GeneratedSources[0].content;
    }

    /// <summary>
    ///     运行 CQRS handler registry generator，并返回生成输出及相关诊断。
    /// </summary>
    /// <param name="source">输入源码。</param>
    /// <param name="allowUnsafe">
    ///     是否允许测试编译包含 <c>unsafe</c> 代码。
    ///     某些回归用例会故意构造带指针类型的非法 handler 合同，以覆盖 fallback 防御分支，此时需要启用该选项避免把缺少
    ///     <c>unsafe</c> 编译上下文的错误与目标生成器行为混淆。
    /// </param>
    /// <param name="additionalReferences">附加元数据引用，用于构造跨程序集场景。</param>
    /// <returns>包含生成源、生成器诊断和更新后编译诊断的执行结果。</returns>
    private static GeneratorExecutionResult ExecuteGenerator(
        string source,
        bool allowUnsafe = false,
        params MetadataReference[] additionalReferences)
    {
        var syntaxTree = CSharpSyntaxTree.ParseText(source);
        var compilation = CSharpCompilation.Create(
            "TestProject",
            [syntaxTree],
            MetadataReferenceTestBuilder.GetRuntimeMetadataReferences().AddRange(additionalReferences),
            new CSharpCompilationOptions(
                OutputKind.DynamicallyLinkedLibrary,
                allowUnsafe: allowUnsafe));

        GeneratorDriver driver = CSharpGeneratorDriver.Create(
            generators: [new CqrsHandlerRegistryGenerator().AsSourceGenerator()],
            parseOptions: (CSharpParseOptions)syntaxTree.Options);
        driver = driver.RunGeneratorsAndUpdateCompilation(
            compilation,
            out var updatedCompilation,
            out var generatorDiagnostics);

        var runResult = driver.GetRunResult();
        Assert.That(runResult.Results, Has.Length.EqualTo(1));
        var generatedSyntaxTrees = runResult.Results[0].GeneratedSources
            .Select(static sourceResult => sourceResult.SyntaxTree)
            .ToHashSet();
        var generatedSources = runResult.Results[0].GeneratedSources
            .Select(static sourceResult =>
                (filename: sourceResult.HintName, content: sourceResult.SourceText.ToString()))
            .ToArray();
        var compilationDiagnostics = updatedCompilation.GetDiagnostics().ToArray();
        var generatedCompilationDiagnostics = compilationDiagnostics
            .Where(diagnostic =>
                diagnostic.Location.SourceTree is not null &&
                generatedSyntaxTrees.Contains(diagnostic.Location.SourceTree))
            .ToArray();
        return new GeneratorExecutionResult(
            generatedSources,
            generatorDiagnostics.ToArray(),
            compilationDiagnostics,
            generatedCompilationDiagnostics);
    }

    /// <summary>
    ///     封装 CQRS handler registry generator 的单次执行结果。
    /// </summary>
    /// <param name="GeneratedSources">本轮生成产生的源文件集合。</param>
    /// <param name="GeneratorDiagnostics">生成器自身报告的诊断集合。</param>
    /// <param name="CompilationDiagnostics">将生成结果并回编译后的完整编译诊断集合。</param>
    /// <param name="GeneratedCompilationDiagnostics">仅来自生成源文件的编译诊断集合。</param>
    private sealed record GeneratorExecutionResult(
        (string filename, string content)[] GeneratedSources,
        Diagnostic[] GeneratorDiagnostics,
        Diagnostic[] CompilationDiagnostics,
        Diagnostic[] GeneratedCompilationDiagnostics);
}
