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);
        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>_contextSync</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);
        GenerateInterfaceImplementations(sb, iContextAware);

        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>
    private static void GenerateContextProperty(StringBuilder sb)
    {
        sb.AppendLine("    private global::GFramework.Core.Abstractions.Architectures.IArchitectureContext? _context;");
        sb.AppendLine(
            "    private static global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider? _contextProvider;");
        sb.AppendLine("    private static readonly object _contextSync = new();");
        sb.AppendLine();
        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>_contextSync</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("            var context = _context;");
        sb.AppendLine("            if (context is not null)");
        sb.AppendLine("            {");
        sb.AppendLine("                return context;");
        sb.AppendLine("            }");
        sb.AppendLine();
        sb.AppendLine("            // 在同一个同步域内协调懒加载与 provider 切换，避免读取到被并发重置的空提供者。");
        sb.AppendLine(
            "            // provider 的 GetContext() 会在持有 _contextSync 时执行；自定义 provider 必须避免在该调用链内回调 SetContextProvider/ResetContextProvider 或形成反向锁顺序。");
        sb.AppendLine("            lock (_contextSync)");
        sb.AppendLine("            {");
        sb.AppendLine(
            "                _contextProvider ??= new global::GFramework.Core.Architectures.GameContextProvider();");
        sb.AppendLine("                _context ??= _contextProvider.GetContext();");
        sb.AppendLine("                return _context;");
        sb.AppendLine("            }");
        sb.AppendLine("        }");
        sb.AppendLine("    }");
        sb.AppendLine();
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 配置当前生成类型共享的上下文提供者。");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    /// <param name=\"provider\">后续懒加载上下文时要使用的提供者实例。</param>");
        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("        lock (_contextSync)");
        sb.AppendLine("        {");
        sb.AppendLine("            _contextProvider = 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 (_contextSync)");
        sb.AppendLine("        {");
        sb.AppendLine("            _contextProvider = 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)
    {
        var interfaceName = interfaceSymbol.ToDisplayString(
            SymbolDisplayFormat.FullyQualifiedFormat);

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

            GenerateMethod(sb, interfaceName, method);
            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)
    {
        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);

        sb.AppendLine("    }");
    }

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