// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

using GFramework.Core.SourceGenerators.Diagnostics;
using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Diagnostics;
using GFramework.SourceGenerators.Common.Generator;

namespace GFramework.Core.SourceGenerators.Rule;

/// <summary>
///     上下文感知生成器，用于为标记了ContextAware特性的类自动生成IContextAware接口实现
/// </summary>
[Generator]
public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
{
    /// <summary>
    ///     获取特性的元数据名称
    /// </summary>
    protected override string AttributeMetadataName =>
        $"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.ContextAwareAttribute";

    /// <summary>
    ///     获取特性的短名称（不包含后缀）
    /// </summary>
    protected override string AttributeShortNameWithoutSuffix => "ContextAware";

    /// <summary>
    ///     验证符号是否符合生成条件
    /// </summary>
    /// <param name="context">源生产上下文</param>
    /// <param name="compilation">编译对象</param>
    /// <param name="syntax">类声明语法节点</param>
    /// <param name="symbol">命名类型符号</param>
    /// <param name="attr">特性数据</param>
    /// <returns>验证是否通过</returns>
    protected override bool ValidateSymbol(
        SourceProductionContext context,
        Compilation compilation,
        ClassDeclarationSyntax syntax,
        INamedTypeSymbol symbol,
        AttributeData attr)
    {
        // 1. 必须是 partial
        if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
        {
            context.ReportDiagnostic(Diagnostic.Create(
                CommonDiagnostics.ClassMustBePartial,
                syntax.Identifier.GetLocation(),
                symbol.Name));
            return false;
        }

        // 2. 必须是 class
        if (symbol.TypeKind != TypeKind.Class)
        {
            context.ReportDiagnostic(Diagnostic.Create(
                ContextAwareDiagnostic.ContextAwareOnlyForClass,
                syntax.Identifier.GetLocation(),
                symbol.Name));
            return false;
        }

        return true;
    }


    /// <summary>
    ///     生成源代码
    /// </summary>
    /// <param name="context">源生产上下文</param>
    /// <param name="compilation">编译对象</param>
    /// <param name="symbol">命名类型符号</param>
    /// <param name="attr">特性数据</param>
    /// <returns>生成的源代码字符串</returns>
    protected override string Generate(
        SourceProductionContext context,
        Compilation compilation,
        INamedTypeSymbol symbol,
        AttributeData attr)
    {
        var ns = symbol.ContainingNamespace.IsGlobalNamespace
            ? null
            : symbol.ContainingNamespace.ToDisplayString();

        var iContextAware = compilation.GetTypeByMetadataName(
            $"{PathContests.CoreAbstractionsNamespace}.Rule.IContextAware")!;

        var sb = new StringBuilder();
        sb.AppendLine("// <auto-generated/>");
        sb.AppendLine("#nullable enable");
        sb.AppendLine();

        if (ns is not null)
        {
            sb.AppendLine($"namespace {ns};");
            sb.AppendLine();
        }

        var interfaceName = iContextAware.ToDisplayString(
            SymbolDisplayFormat.FullyQualifiedFormat);
        var memberNames = CreateGeneratedContextMemberNames(symbol);
        sb.AppendLine("/// <summary>");
        sb.AppendLine("/// 为当前规则类型补充自动生成的架构上下文访问实现。");
        sb.AppendLine("/// </summary>");
        sb.AppendLine("/// <remarks>");
        sb.AppendLine(
            "/// 生成代码会在实例级缓存首次解析到的上下文，并在未显式配置提供者时回退到 <see cref=\"GFramework.Core.Architectures.GameContextProvider\" />。");
        sb.AppendLine(
            "/// 同一生成类型的所有实例共享一个静态上下文提供者；切换或重置提供者只会影响尚未缓存上下文的新实例或未初始化实例，");
        sb.AppendLine(
            "/// 已缓存的实例上下文需要通过 <see cref=\"GFramework.Core.Abstractions.Rule.IContextAware.SetContext(GFramework.Core.Abstractions.Architectures.IArchitectureContext)\" /> 显式覆盖。");
        sb.AppendLine(
            $"/// 与手动继承 <see cref=\"global::GFramework.Core.Rule.ContextAwareBase\" /> 的路径相比，生成实现会使用 <c>{memberNames.SyncFieldName}</c> 协调惰性初始化、provider 切换和显式上下文注入；");
        sb.AppendLine(
            "/// <see cref=\"global::GFramework.Core.Rule.ContextAwareBase\" /> 则保持无锁的实例级缓存语义，更适合已经由调用方线程模型保证串行访问的简单场景。");
        sb.AppendLine("/// </remarks>");
        sb.AppendLine($"partial class {symbol.Name} : {interfaceName}");
        sb.AppendLine("{");

        GenerateContextProperty(sb, memberNames);
        GenerateInterfaceImplementations(sb, iContextAware, memberNames);

        sb.AppendLine("}");
        return sb.ToString().TrimEnd();
    }

    /// <summary>
    ///     获取生成文件的提示名称
    /// </summary>
    /// <param name="symbol">命名类型符号</param>
    /// <returns>生成文件的提示名称</returns>
    protected override string GetHintName(INamedTypeSymbol symbol)
    {
        return $"{symbol.Name}.ContextAware.g.cs";
    }

    // =========================
    // Context 属性（使用 IArchitectureContextProvider）
    // =========================
    /// <summary>
    ///     生成Context属性
    /// </summary>
    /// <param name="sb">字符串构建器</param>
    /// <param name="memberNames">当前目标类型应使用的上下文字段名。</param>
    private static void GenerateContextProperty(
        StringBuilder sb,
        GeneratedContextMemberNames memberNames)
    {
        GenerateContextBackingFields(sb, memberNames);
        GenerateContextGetter(sb, memberNames);
        GenerateContextProviderConfiguration(sb, memberNames);
    }

    /// <summary>
    ///     生成上下文缓存和同步所需的字段。
    /// </summary>
    /// <param name="sb">字符串构建器。</param>
    private static void GenerateContextBackingFields(
        StringBuilder sb,
        GeneratedContextMemberNames memberNames)
    {
        sb.AppendLine(
            $"    private global::GFramework.Core.Abstractions.Architectures.IArchitectureContext? {memberNames.ContextFieldName};");
        sb.AppendLine(
            $"    private static global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider? {memberNames.ProviderFieldName};");
        sb.AppendLine($"    private static readonly object {memberNames.SyncFieldName} = new();");
        sb.AppendLine();
    }

    /// <summary>
    ///     生成实例上下文访问器，包含显式注入优先和 provider 惰性回退语义。
    /// </summary>
    /// <param name="sb">字符串构建器。</param>
    private static void GenerateContextGetter(
        StringBuilder sb,
        GeneratedContextMemberNames memberNames)
    {
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 获取当前实例绑定的架构上下文。");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    /// <remarks>");
        sb.AppendLine(
            "    /// 该属性会先返回通过 <c>IContextAware.SetContext(...)</c> 显式注入的实例上下文；若尚未设置，则在同一个同步域内惰性初始化共享提供者。");
        sb.AppendLine(
            "    /// 当静态提供者尚未配置时，生成代码会回退到 <see cref=\"GFramework.Core.Architectures.GameContextProvider\" />。");
        sb.AppendLine(
            "    /// 一旦某个实例成功缓存上下文，后续 <see cref=\"SetContextProvider(GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider)\" />");
        sb.AppendLine(
            "    /// 或 <see cref=\"ResetContextProvider\" /> 不会自动清除此缓存；如需覆盖，请显式调用 <c>IContextAware.SetContext(...)</c>。");
        sb.AppendLine(
            $"    /// 当前实现还假设 <see cref=\"GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider.GetContext\" /> 可在持有 <c>{memberNames.SyncFieldName}</c> 时安全执行；");
        sb.AppendLine(
            "    /// 自定义 provider 不应在该调用链内重新进入当前类型的 provider 配置 API，且应避免引入与外部全局锁相互等待的锁顺序。");
        sb.AppendLine("    /// </remarks>");
        sb.AppendLine("    protected global::GFramework.Core.Abstractions.Architectures.IArchitectureContext Context");
        sb.AppendLine("    {");
        sb.AppendLine("        get");
        sb.AppendLine("        {");
        sb.AppendLine("            // 在同一个同步域内协调懒加载与 provider 切换，避免读取到被并发重置的空提供者。");
        sb.AppendLine(
            $"            // provider 的 GetContext() 会在持有 {memberNames.SyncFieldName} 时执行；自定义 provider 必须避免在该调用链内回调 SetContextProvider/ResetContextProvider 或形成反向锁顺序。");
        sb.AppendLine($"            lock ({memberNames.SyncFieldName})");
        sb.AppendLine("            {");
        sb.AppendLine(
            $"                {memberNames.ProviderFieldName} ??= new global::GFramework.Core.Architectures.GameContextProvider();");
        sb.AppendLine($"                {memberNames.ContextFieldName} ??= {memberNames.ProviderFieldName}.GetContext();");
        sb.AppendLine($"                return {memberNames.ContextFieldName};");
        sb.AppendLine("            }");
        sb.AppendLine("        }");
        sb.AppendLine("    }");
        sb.AppendLine();
    }

    /// <summary>
    ///     生成静态 provider 配置 API，供测试和宿主在懒加载前替换默认上下文来源。
    /// </summary>
    /// <param name="sb">字符串构建器。</param>
    private static void GenerateContextProviderConfiguration(
        StringBuilder sb,
        GeneratedContextMemberNames memberNames)
    {
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 配置当前生成类型共享的上下文提供者。");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    /// <param name=\"provider\">后续懒加载上下文时要使用的提供者实例。</param>");
        sb.AppendLine(
            "    /// <exception cref=\"global::System.ArgumentNullException\">当 <paramref name=\"provider\" /> 为 null 时抛出。</exception>");
        sb.AppendLine("    /// <remarks>");
        sb.AppendLine("    /// 该方法使用与 <see cref=\"Context\" /> 相同的同步锁，避免提供者切换与惰性初始化交错。");
        sb.AppendLine(
            "    /// 已经缓存上下文的实例不会因为提供者切换而自动失效；该变更仅影响尚未初始化上下文的新实例或未缓存实例。");
        sb.AppendLine("    /// 如需覆盖已有实例的上下文，请显式调用 <c>IContextAware.SetContext(...)</c>。");
        sb.AppendLine("    /// </remarks>");
        sb.AppendLine(
            "    public static void SetContextProvider(global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider provider)");
        sb.AppendLine("    {");
        sb.AppendLine("        global::System.ArgumentNullException.ThrowIfNull(provider);");
        sb.AppendLine($"        lock ({memberNames.SyncFieldName})");
        sb.AppendLine("        {");
        sb.AppendLine($"            {memberNames.ProviderFieldName} = provider;");
        sb.AppendLine("        }");
        sb.AppendLine("    }");
        sb.AppendLine();
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 重置共享上下文提供者，使后续懒加载回退到默认提供者。");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    /// <remarks>");
        sb.AppendLine("    /// 该方法主要用于测试清理或跨用例恢复默认行为。");
        sb.AppendLine(
            "    /// 它不会清除已经缓存到实例字段中的上下文；只有后续尚未初始化上下文的实例会重新回退到 <see cref=\"GFramework.Core.Architectures.GameContextProvider\" />。");
        sb.AppendLine("    /// 如需覆盖已有实例的上下文，请显式调用 <c>IContextAware.SetContext(...)</c>。");
        sb.AppendLine("    /// </remarks>");
        sb.AppendLine("    public static void ResetContextProvider()");
        sb.AppendLine("    {");
        sb.AppendLine($"        lock ({memberNames.SyncFieldName})");
        sb.AppendLine("        {");
        sb.AppendLine($"            {memberNames.ProviderFieldName} = null;");
        sb.AppendLine("        }");
        sb.AppendLine("    }");
        sb.AppendLine();
    }


    // =========================
    // 显式接口实现（使用 global::）
    // =========================
    /// <summary>
    ///     生成接口实现
    /// </summary>
    /// <param name="sb">字符串构建器</param>
    /// <param name="interfaceSymbol">接口符号</param>
    private static void GenerateInterfaceImplementations(
        StringBuilder sb,
        INamedTypeSymbol interfaceSymbol,
        GeneratedContextMemberNames memberNames)
    {
        var interfaceName = interfaceSymbol.ToDisplayString(
            SymbolDisplayFormat.FullyQualifiedFormat);

        foreach (var method in interfaceSymbol.GetMembers().OfType<IMethodSymbol>())
        {
            if (method.MethodKind != MethodKind.Ordinary)
                continue;

            GenerateMethod(sb, interfaceName, method, memberNames);
            sb.AppendLine();
        }
    }

    /// <summary>
    ///     生成方法实现
    /// </summary>
    /// <param name="sb">字符串构建器</param>
    /// <param name="interfaceName">接口名称</param>
    /// <param name="method">方法符号</param>
    private static void GenerateMethod(
        StringBuilder sb,
        string interfaceName,
        IMethodSymbol method,
        GeneratedContextMemberNames memberNames)
    {
        var returnType = method.ReturnType.ToDisplayString(
            SymbolDisplayFormat.FullyQualifiedFormat);

        var parameters = string.Join(", ",
            method.Parameters.Select(p =>
                $"{p.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {p.Name}"));

        sb.AppendLine(
            $"    {returnType} {interfaceName}.{method.Name}({parameters})");
        sb.AppendLine("    {");

        GenerateMethodBody(sb, method, memberNames);

        sb.AppendLine("    }");
    }

    /// <summary>
    ///     生成方法体
    /// </summary>
    /// <param name="sb">字符串构建器</param>
    /// <param name="method">方法符号</param>
    private static void GenerateMethodBody(
        StringBuilder sb,
        IMethodSymbol method,
        GeneratedContextMemberNames memberNames)
    {
        switch (method.Name)
        {
            case "SetContext":
                sb.AppendLine("        // 与 Context getter 共享同一同步协议，避免显式注入被并发懒加载覆盖。");
                sb.AppendLine($"        lock ({memberNames.SyncFieldName})");
                sb.AppendLine("        {");
                sb.AppendLine($"            {memberNames.ContextFieldName} = context;");
                sb.AppendLine("        }");
                break;

            case "GetContext":
                sb.AppendLine("        return Context;");
                break;

            default:
                if (!method.ReturnsVoid)
                    sb.AppendLine(
                        $"        throw new System.NotImplementedException(\"Method '{method.Name}' is not supported.\");");

                break;
        }
    }

    /// <summary>
    ///     为生成字段选择不会与目标类型现有成员冲突的稳定名称。
    /// </summary>
    /// <param name="symbol">当前需要补充 ContextAware 实现的目标类型。</param>
    /// <returns>当前生成轮次应使用的上下文字段名集合。</returns>
    private static GeneratedContextMemberNames CreateGeneratedContextMemberNames(INamedTypeSymbol symbol)
    {
        var reservedNames = CollectReservedContextMemberNames(symbol);

        return new GeneratedContextMemberNames(
            AllocateGeneratedMemberName(reservedNames, "_gFrameworkContextAwareContext"),
            AllocateGeneratedMemberName(reservedNames, "_gFrameworkContextAwareProvider"),
            AllocateGeneratedMemberName(reservedNames, "_gFrameworkContextAwareSync"));
    }

    /// <summary>
    ///     收集当前类型及其基类链上所有显式声明的成员名，确保生成字段不会意外隐藏继承成员。
    /// </summary>
    /// <param name="symbol">当前需要补充 ContextAware 实现的目标类型。</param>
    /// <returns>已被当前类型层级占用的成员名集合。</returns>
    private static HashSet<string> CollectReservedContextMemberNames(INamedTypeSymbol symbol)
    {
        var reservedNames = new HashSet<string>(StringComparer.Ordinal);

        // Walk the full inheritance chain so numeric suffix allocation also covers members introduced by base types.
        for (var currentType = symbol; currentType is not null; currentType = currentType.BaseType)
        {
            foreach (var member in currentType.GetMembers())
            {
                if (!member.IsImplicitlyDeclared)
                {
                    reservedNames.Add(member.Name);
                }
            }
        }

        return reservedNames;
    }

    /// <summary>
    ///     在固定前缀基础上按顺序追加数字后缀，直到找到可安全写入的成员名。
    /// </summary>
    /// <param name="reservedNames">当前类型已占用或已为其他生成字段保留的名称集合。</param>
    /// <param name="baseName">优先尝试的基础名称。</param>
    /// <returns>本轮生成可以使用的唯一成员名。</returns>
    private static string AllocateGeneratedMemberName(
        ISet<string> reservedNames,
        string baseName)
    {
        if (reservedNames.Add(baseName))
            return baseName;

        for (var suffix = 1; ; suffix++)
        {
            var candidateName = $"{baseName}{suffix}";
            if (reservedNames.Add(candidateName))
                return candidateName;
        }
    }

    /// <summary>
    ///     描述一次 ContextAware 代码生成中选定的上下文字段名。
    /// </summary>
    /// <param name="ContextFieldName">实例上下文缓存字段名。</param>
    /// <param name="ProviderFieldName">共享上下文提供者字段名。</param>
    /// <param name="SyncFieldName">用于串行化访问的同步字段名。</param>
    private readonly record struct GeneratedContextMemberNames(
        string ContextFieldName,
        string ProviderFieldName,
        string SyncFieldName);
}
