using System.Text;
using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Diagnostics;
using GFramework.SourceGenerators.Common.Generator;
using GFramework.SourceGenerators.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace GFramework.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($"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();
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 自动获取的架构上下文（懒加载，默认使用 GameContextProvider）");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    protected global::GFramework.Core.Abstractions.Architectures.IArchitectureContext Context");
        sb.AppendLine("    {");
        sb.AppendLine("        get");
        sb.AppendLine("        {");
        sb.AppendLine("            if (_context == null)");
        sb.AppendLine("            {");
        sb.AppendLine(
            "                _contextProvider ??= new global::GFramework.Core.Architectures.GameContextProvider();");
        sb.AppendLine("                _context = _contextProvider.GetContext();");
        sb.AppendLine("            }");
        sb.AppendLine();
        sb.AppendLine("            return _context;");
        sb.AppendLine("        }");
        sb.AppendLine("    }");
        sb.AppendLine();
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 配置上下文提供者（用于测试或多架构场景）");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    /// <param name=\"provider\">上下文提供者实例</param>");
        sb.AppendLine(
            "    public static void SetContextProvider(global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider provider)");
        sb.AppendLine("    {");
        sb.AppendLine("        _contextProvider = provider;");
        sb.AppendLine("    }");
        sb.AppendLine();
        sb.AppendLine("    /// <summary>");
        sb.AppendLine("    /// 重置上下文提供者为默认值（用于测试清理）");
        sb.AppendLine("    /// </summary>");
        sb.AppendLine("    public static void ResetContextProvider()");
        sb.AppendLine("    {");
        sb.AppendLine("        _contextProvider = null;");
        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 = context;");
                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;
        }
    }
}