using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace PCL.Core.SourceGenerators;

[Generator(LanguageNames.CSharp)]
public sealed class ConfigGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 收集所有可能的属性与类
        var propertyCandidates = context.SyntaxProvider.CreateSyntaxProvider(
            static (s, _) => s is PropertyDeclarationSyntax { AttributeLists.Count: > 0 },
            static (ctx, _) => _GetItemCandidate(ctx)
        ).Where(static m => m is not null);

        var groupCandidates = context.SyntaxProvider.CreateSyntaxProvider(
            static (s, _) => s is ClassDeclarationSyntax { AttributeLists.Count: > 0 },
            static (ctx, _) => _GetGroupCandidate(ctx)
        ).Where(static m => m is not null);

        var configClassCandidates = context.SyntaxProvider.CreateSyntaxProvider(
            static (s, _) => s is ClassDeclarationSyntax,
            static (ctx, _) => _GetConfigClass(ctx)
        ).Where(static m => m is not null);

        // 新增：收集 [RegisterConfigEvent] 的 public static 属性
        var eventCandidates = context.SyntaxProvider.CreateSyntaxProvider(
            static (s, _) => s is PropertyDeclarationSyntax { AttributeLists.Count: > 0 },
            static (ctx, _) => _GetRegisterConfigEventCandidate(ctx)
        ).Where(static m => m is not null);

        var collected = propertyCandidates.Collect()
            .Combine(groupCandidates.Collect())
            .Combine(configClassCandidates.Collect());

        context.RegisterSourceOutput(collected, static (spc, triple) =>
        {
            var items = triple.Left.Left;
            var groups = triple.Left.Right;
            var configs = triple.Right;

            if (configs.Length == 0) return;

            // 建立快速查找
            var itemList = items.Cast<ItemModel>().ToImmutableArray();
            var groupList = groups.Cast<GroupModel>().ToImmutableArray();
            var configList = configs.Cast<ConfigModel>().ToImmutableArray();

            foreach (var config in configList)
            {
                try
                {
                    var tree = _BuildConfigTree(config, itemList, groupList);

                    // 跳过无用生成（无顶层项和顶层组声明的类型）
                    if (tree.TopItems.Count == 0 && tree.TopGroups.Count == 0) continue;

                    var source = _GenerateAdditionalSource(tree);
                    var hint = _MakeHintName(config);
                    spc.AddSource(hint, source);
                }
                catch
                {
                    // 可添加诊断，此处直接忽略以免打断编译
                }
            }
        });

        var serviceInputs = propertyCandidates.Collect()
            .Combine(groupCandidates.Collect())
            .Combine(eventCandidates.Collect());
        context.RegisterSourceOutput(serviceInputs, static (spc, tuple) =>
        {
            var items = tuple.Left.Left.Cast<ItemModel>().OrderBy(i => i.DeclOrder).ToList();
            var groups = tuple.Left.Right.Cast<GroupModel>().ToList();
            var events = tuple.Right.Cast<EventRegisterModel>().OrderBy(e => e.DeclOrder).ToList();

            // 两者都为空则不生成
            if (items.Count == 0 && events.Count == 0) return;

            var groupLookup = new Dictionary<INamedTypeSymbol, GroupModel>(SymbolEqualityComparer.Default);
            foreach (var group in groups)
            {
                groupLookup[group.GroupType] = group;
            }

            var src = _GenerateServiceInitSource(items, events, groupLookup);
            spc.AddSource("ConfigService.g.cs", src);
        });
    }

    private static object? _GetItemCandidate(GeneratorSyntaxContext ctx)
    {
        var propSyntax = (PropertyDeclarationSyntax)ctx.Node;
        var symbol = ctx.SemanticModel.GetDeclaredSymbol(propSyntax);
        if (symbol is null) return null;

        var compilation = ctx.SemanticModel.Compilation;
        var attrDefItem = compilation.GetTypeByMetadataName("PCL.Core.App.Configuration.ConfigItemAttribute`1");
        var attrDefAny  = compilation.GetTypeByMetadataName("PCL.Core.App.Configuration.AnyConfigItemAttribute`1");
        if (attrDefItem is null && attrDefAny is null) return null;

        AttributeData? picked = null;
        var isAny = false;

        foreach (var a in symbol.GetAttributes())
        {
            var ac = a.AttributeClass;
            if (ac is null) continue;
            if (attrDefItem is not null && SymbolEqualityComparer.Default.Equals(ac.ConstructedFrom, attrDefItem))
            {
                picked = a; isAny = false; break;
            }
            if (attrDefAny is not null && SymbolEqualityComparer.Default.Equals(ac.ConstructedFrom, attrDefAny))
            {
                picked = a; isAny = true; break;
            }
        }
        if (picked is null) return null;

        if (picked.ConstructorArguments.Length < 1) return null;
        var key = picked.ConstructorArguments[0].Value as string;
        if (string.IsNullOrEmpty(key)) return null;

        // 默认值与来源解析
        var defaultCode = "default";
        string? sourceCode = null;

        var attrSyntax = (AttributeSyntax?)picked.ApplicationSyntaxReference?.GetSyntax();
        if (attrSyntax is not null)
        {
            var args = attrSyntax.ArgumentList?.Arguments;

            if (isAny)
            {
                // AnyConfigItem：没有“默认值”参数: 替换为无参构造函数
                var tQualified = symbol.Type.GetFullyQualifiedName();
                defaultCode = "() => new " + tQualified + "()";

                // 来源参数若存在，是第 2 个实参
                if (args is { Count: >= 2 })
                {
                    sourceCode = _RenderSourceCode(ctx.SemanticModel, args.Value[1].Expression);
                }
            }
            else
            {
                // ConfigItem：参数2为默认值，参数3为来源（可省略）
                if (args is { Count: >= 2 })
                {
                    defaultCode = ctx.SemanticModel.RenderDefaultValueCode(args.Value[1].Expression);
                }
                if (args is { Count: >= 3 })
                {
                    sourceCode = _RenderSourceCode(ctx.SemanticModel, args.Value[2].Expression);
                }
            }
        }

        return new ItemModel
        {
            Property = symbol,
            Key = key!,
            Type = symbol.Type,
            IsStatic = symbol.IsStatic,
            DeclOrder = symbol.GetDeclarationOrder(),
            DefaultValueCode = defaultCode,
            SourceCode = sourceCode
        };
    }

    private static object? _GetGroupCandidate(GeneratorSyntaxContext ctx)
    {
        var classSyntax = (ClassDeclarationSyntax)ctx.Node;
        var symbol = ModelExtensions.GetDeclaredSymbol(ctx.SemanticModel, classSyntax) as INamedTypeSymbol;
        if (symbol is null) return null;

        var compilation = ctx.SemanticModel.Compilation;
        var attrDef = compilation.GetTypeByMetadataName("PCL.Core.App.Configuration.ConfigGroupAttribute");
        if (attrDef is null) return null;

        var attr = symbol.GetAttributes().FirstOrDefault(a =>
            a.AttributeClass is not null &&
            SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrDef));

        if (attr is null) return null;

        if (attr.ConstructorArguments.Length < 1) return null;
        var name = attr.ConstructorArguments[0].Value as string;
        if (string.IsNullOrEmpty(name)) return null;

        var hasDeclaredSource = false;
        string? declaredSourceCode = null;
        var attrSyntax = (AttributeSyntax?)attr.ApplicationSyntaxReference?.GetSyntax();
        if (attrSyntax?.ArgumentList?.Arguments is { Count: >= 2 } arguments)
        {
            hasDeclaredSource = true;
            declaredSourceCode = _RenderSourceCode(ctx.SemanticModel, arguments[1].Expression);
        }

        return new GroupModel
        {
            GroupType = symbol,
            GroupName = name!,
            DeclOrder = symbol.GetDeclarationOrder(),
            HasDeclaredSource = hasDeclaredSource,
            DeclaredSourceCode = declaredSourceCode
        };
    }

    private static object? _GetRegisterConfigEventCandidate(GeneratorSyntaxContext ctx)
    {
        var propSyntax = (PropertyDeclarationSyntax)ctx.Node;
        if (ctx.SemanticModel.GetDeclaredSymbol(propSyntax) is not { } symbol) return null;

        // 仅 public static
        if (symbol.DeclaredAccessibility != Accessibility.Public || !symbol.IsStatic) return null;

        // 精确匹配 [RegisterConfigEvent]
        var compilation = ctx.SemanticModel.Compilation;
        var attrDef = compilation.GetTypeByMetadataName("PCL.Core.App.Configuration.RegisterConfigEventAttribute");
        if (attrDef is null) return null;
        var hasAttr = symbol.GetAttributes().Any(a =>
            a.AttributeClass is not null &&
            SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrDef));
        if (!hasAttr) return null;

        return new EventRegisterModel
        {
            Property = symbol,
            DeclOrder = symbol.GetDeclarationOrder()
        };
    }

    private static object? _GetConfigClass(GeneratorSyntaxContext ctx)
    {
        var classSyntax = (ClassDeclarationSyntax)ctx.Node;
        if (ModelExtensions.GetDeclaredSymbol(ctx.SemanticModel, classSyntax) is not INamedTypeSymbol symbol) return null;

        // 限定为 partial
        if (!symbol.IsPartial()) return null;

        // 绕过 [ConfigGroup]
        var compilation = ctx.SemanticModel.Compilation;
        var attrDef = compilation.GetTypeByMetadataName("PCL.Core.App.Configuration.ConfigGroupAttribute");
        if (attrDef is not null)
        {
            var hasAttr = symbol.GetAttributes().Any(a =>
                a.AttributeClass is not null &&
                SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrDef));
            if (hasAttr) return null;
        }

        return new ConfigModel
        {
            ConfigType = symbol,
            DeclOrder = symbol.GetDeclarationOrder()
        };
    }

    private static ConfigTree _BuildConfigTree(ConfigModel config,
        ImmutableArray<ItemModel> items,
        ImmutableArray<GroupModel> groups)
    {
        var configType = config.ConfigType;

        // 过滤归属于该 Config 的顶层项与组
        var topItems = items.Where(i => SymbolEqualityComparer.Default.Equals(i.Property.ContainingType, configType))
                            .OrderBy(i => i.DeclOrder)
                            .ToList();

        var allGroupsForConfig = groups
            .Where(g => g.GroupType.IsNestedWithin(configType))
            .OrderBy(g => g.DeclOrder)
            .ToList();

        // 构建组索引
        var groupMap = allGroupsForConfig.ToDictionary(g => g.GroupType, g => new GroupNode(g), SymbolEqualityComparer.Default);

        GroupModel? GroupLookup(INamedTypeSymbol type) =>
            groupMap.TryGetValue(type, out var node) ? node.Model : null;

        string ResolveItemSource(ItemModel item) =>
            _ResolveItemSourceCode(item, GroupLookup);

        // 组装层级
        foreach (var node in groupMap.Values)
        {
            var parentType = node.Model.GroupType.ContainingType;
            if (parentType is not null && !SymbolEqualityComparer.Default.Equals(parentType, configType))
            {
                if (groupMap.TryGetValue(parentType, out var parentNode))
                {
                    parentNode.Children.Add(node);
                }
            }
        }

        // 顶层组
        var topGroups = groupMap.Values
            .Where(n => SymbolEqualityComparer.Default.Equals(n.Model.GroupType.ContainingType, configType))
            .OrderBy(n => n.Model.DeclOrder)
            .ToList();

        // 将 Item 分配到各自的组
        foreach (var item in items.Except(topItems))
        {
            var container = item.Property.ContainingType;
            if (container is null) continue;
            if (groupMap.TryGetValue(container, out var groupNode))
            {
                groupNode.Items.Add(item);
            }
        }

        return new ConfigTree
        {
            Namespace = configType.ContainingNamespace?.ToDisplayString() ?? "",
            ConfigType = configType,
            TopItems = topItems,
            TopGroups = topGroups,
            ResolveSourceCode = ResolveItemSource
        };
    }

    private static string _MakeHintName(ConfigModel config)
    {
        var ns = config.ConfigType.ContainingNamespace?.ToDisplayString() ?? "Global";
        return $"{ns}.{config.ConfigType.Name}.g.cs";
    }

    private static string _GenerateAdditionalSource(ConfigTree tree)
    {
        var sb = new StringBuilder(4096);

        var ns = string.IsNullOrEmpty(tree.Namespace) ? null : tree.Namespace;
        var configType = tree.ConfigType;
        var configName = configType.Name;
        var resolveSource = tree.ResolveSourceCode;

        sb.AppendLine("// <auto-generated />");
        sb.AppendLine("// 此文件由 Source Generator 自动生成，请勿手动修改");
        sb.AppendLine();
        sb.AppendLine("using System.Collections.Generic;");
        sb.AppendLine("using System.Linq;");
        sb.AppendLine("using PCL.Core.App.Configuration;");
        sb.AppendLine();
        sb.AppendLine("#nullable enable");
        sb.AppendLine();

        if (!string.IsNullOrEmpty(ns))
        {
            sb.Append("namespace ").Append(ns).AppendLine(";");
            sb.AppendLine();
        }

        sb.Append("partial class ").Append(configName).AppendLine();
        sb.AppendLine("{");

        // === Config Items ===
        sb.AppendLine("    // === Config Items ===");
        sb.AppendLine();
        if (tree.TopItems.Count == 0)
        {
            sb.AppendLine();
        }
        else
        {
            foreach (var item in tree.TopItems)
            {
                _EmitItem(sb, item, indent: 1, isTopLevel: true, resolveSource);
                sb.AppendLine();
            }
        }

        // === Config Groups ===
        sb.AppendLine("    // === Config Groups ===");
        if (tree.TopGroups.Count > 0)
        {
            foreach (var grp in tree.TopGroups)
            {
                _EmitGroupInto(sb, grp, indent: 1, isTopLevel: true, resolveSource);
            }
        }

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

    private static string _GenerateServiceInitSource(
        IReadOnlyList<ItemModel> items,
        IReadOnlyList<EventRegisterModel> events,
        IReadOnlyDictionary<INamedTypeSymbol, GroupModel> groupLookup)
    {
        GroupModel? Lookup(INamedTypeSymbol type) =>
            groupLookup.TryGetValue(type, out var model) ? model : null;

        string ResolveSource(ItemModel item) =>
            _ResolveItemSourceCode(item, Lookup);

        var sb = new StringBuilder(1024);
        sb.AppendLine("// <auto-generated />");
        sb.AppendLine("// 此文件由 Source Generator 自动生成，请勿手动修改");
        sb.AppendLine();
        sb.AppendLine("namespace PCL.Core.App.Configuration;");
        sb.AppendLine();
        sb.AppendLine("public sealed partial class ConfigService");
        sb.AppendLine("{");

        // 配置项初始化
        sb.AppendLine("    private static void _InitializeConfigItems()");
        sb.AppendLine("    {");
        sb.AppendLine("        (string, ConfigItem)[] items = [");

        HashSet<string> keysAdded = [];
        for (var i = 0; i < items.Count; i++)
        {
            var it = items[i];
            if (!keysAdded.Add(it.Key)) continue;
            var keyLiteral = it.Key.ToLiteral();
            var typeName = it.Type.GetFullyQualifiedName().CorrectConfigTypeName(out _);
            var sourceCode = ResolveSource(it);
            sb.Append("            (")
                .Append(keyLiteral)
                .Append(", new ConfigItem<").Append(typeName).Append(">(")
                .Append(keyLiteral).Append(", ")
                .Append(it.DefaultValueCode).Append(", ").Append(sourceCode)
              .Append("))");
            if (i != items.Count - 1) sb.Append(',');
            sb.AppendLine();
        }

        sb.AppendLine("        ];");
        sb.AppendLine("        foreach (var (key, value) in items) {");
        sb.AppendLine("            _KeySet.Add(key);");
        sb.AppendLine("            _Items[key] = value;");
        sb.AppendLine("        }");
        sb.AppendLine("    }");
        sb.AppendLine();

        // 事件观察器初始化
        sb.AppendLine("    private static void _InitializeObservers()");
        sb.AppendLine("    {");
        sb.AppendLine("        ConfigEventRegistry[] registers = [");

        for (var i = 0; i < events.Count; i++)
        {
            var ev = events[i];
            sb.Append("            ")
              .Append(ev.Property.GetQualifiedPropertyAccess());
            if (i != events.Count - 1) sb.Append(',');
            sb.AppendLine();
        }

        sb.AppendLine("        ];");
        sb.AppendLine("        foreach (var r in registers) foreach (var scope in r.Scopes) {");
        sb.AppendLine("            RegisterObserver(scope, r.ToObserver());");
        sb.AppendLine("        }");
        sb.AppendLine("    }");

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

    private static string _ResolveItemSourceCode(ItemModel item, Func<INamedTypeSymbol, GroupModel?> groupLookup)
    {
        if (item.SourceCode is { } explicitSource)
        {
            return explicitSource;
        }

        var container = item.Property.ContainingType;
        while (container is not null)
        {
            var group = groupLookup(container);
            if (group is { HasDeclaredSource: true, DeclaredSourceCode: { } declared })
            {
                return declared;
            }
            container = container.ContainingType;
        }

        return "ConfigSource.Shared";
    }

    private static Action<StringBuilder>? _EmitItem(
        StringBuilder sb,
        ItemModel item,
        int indent,
        bool isTopLevel,
        Func<ItemModel, string> resolveSource)
    {
        Action<StringBuilder>? accessorInitializer = null;
        var typeName = item.Type.GetFullyQualifiedName().CorrectConfigTypeName(out var fullTypeName);
        var propName = item.Property.Name;
        var configItemName = propName + "Config";
        var staticKeyword = item.IsStatic || isTopLevel ? "static " : string.Empty;
        var indentStr = new string(' ', indent * 4);
        var sourceCode = resolveSource(item);

        // 注释
        sb.Append(indentStr).Append("// Item: ").Append(propName).Append(" [").Append(item.Key).AppendLine("]");

        // 访问器
        sb.Append(indentStr)
          .Append("public ").Append(staticKeyword).Append("partial ")
          .Append(fullTypeName ?? typeName).Append(' ').Append(propName);

        if (fullTypeName is not null)
        {
            var accessorName = "ACCESSOR_" + propName;
            sb.Append(" => ").Append(accessorName).AppendLine(";");
            // 初始化带参数访问器
            sb.Append(indentStr)
              .Append("private ").Append(staticKeyword).Append("readonly ")
              .Append(fullTypeName).Append(' ');
            // ReSharper disable once VariableHidesOuterVariable
            void AccessorInitializer(StringBuilder sb)
            {
                sb.Append(accessorName)
                  .Append(" = new((arg) => ")
                  .Append(configItemName)
                  .Append(".GetValue(arg), (arg, value) => ")
                  .Append(configItemName)
                  .AppendLine(".SetValue(value, arg));");
            }
            if (item.IsStatic) AccessorInitializer(sb);
            else {
                accessorInitializer = AccessorInitializer;
                sb.Append(accessorName).AppendLine(";");
            }
        }
        else
        {
            sb.Append(" { get => ")
              .Append(configItemName)
              .Append(".GetValue(); set => ")
              .Append(configItemName)
              .AppendLine(".SetValue(value); }");
        }

        // 配置项
        sb.Append(indentStr)
          .Append("public ").Append(staticKeyword)
          .Append("ConfigItem<").Append(typeName).Append("> ")
          .Append(configItemName).Append(" { get => field ??= ConfigService.GetConfigItem<")
            .Append(typeName)
          .Append(">(")
            .Append(item.Key.ToLiteral())
          .AppendLine("); } = null!;");

        return accessorInitializer;
    }

    private static void _EmitGroupInto(
        StringBuilder sb,
        GroupNode node,
        int indent,
        bool isTopLevel,
        Func<ItemModel, string> resolveSource)
    {
        var indentStr = new string(' ', indent * 4);
        var type = node.Model.GroupType;
        var typeName = type.Name;
        var staticKeyword = isTopLevel ? "static " : string.Empty;

        // 组实例字段（在其父作用域中）
        sb.AppendLine();
        sb.Append(indentStr).Append("// Group: ").AppendLine(node.Model.GroupName);
        sb.Append(indentStr).Append("/// <inheritdoc cref=\"").Append(typeName).AppendLine("\" />");
        sb.Append(indentStr)
          .Append("public ")
          .Append(staticKeyword)
          .Append("readonly ")
          .Append(typeName)
          .Append(' ')
          .Append(node.Model.GroupName)
          .Append(" = ")
          .Append(typeName)
          .AppendLine(".SINGLE_INSTANCE;");

        // 嵌套类型定义
        sb.Append(indentStr)
          .Append("public sealed partial class ")
          .Append(typeName)
          .AppendLine(" : IConfigScope");
        sb.Append(indentStr).AppendLine("{");

        // === Config Items ===
        sb.Append(indentStr).AppendLine("    // === Config Items ===");
        sb.Append(indentStr).AppendLine();
        List<Action<StringBuilder>> accessorInitializers = [];
        foreach (var item in node.Items.OrderBy(i => i.DeclOrder))
        {
            var result = _EmitItem(sb, item, indent + 1, isTopLevel: false, resolveSource);
            if (result is not null) accessorInitializers.Add(result);
            sb.AppendLine();
        }

        // === Config Groups ===
        sb.Append(indentStr).AppendLine("    // === Config Groups ===");
        foreach (var child in node.Children.OrderBy(c => c.Model.DeclOrder))
        {
            _EmitGroupInto(sb, child, indent + 1, isTopLevel: false, resolveSource);
        }

        // === Group Scope Implementation ===
        sb.AppendLine();
        sb.Append(indentStr).AppendLine("    // === Group Scope Implementation ===");
        sb.Append(indentStr).AppendLine();
        sb.Append(indentStr).AppendLine("    public static readonly " + typeName + " SINGLE_INSTANCE = new();");
        sb.Append(indentStr).AppendLine("    private readonly IConfigScope[] _InnerScopes;");
        sb.Append(indentStr).AppendLine("    private " + typeName + "()");
        sb.Append(indentStr).AppendLine("    {");
        sb.Append(indentStr).AppendLine("        _InnerScopes = [");

        // InnerScopes: 先项再子组
        var first = true;
        foreach (var item in node.Items.SkipWhile(i => i.IsStatic).OrderBy(i => i.DeclOrder))
        {
            if (!first) sb.AppendLine(",");
            sb.Append(indentStr).Append("            ").Append(item.Property.Name).Append("Config");
            first = false;
        }
        foreach (var child in node.Children.OrderBy(c => c.Model.DeclOrder))
        {
            if (!first) sb.AppendLine(",");
            sb.Append(indentStr).Append("            ").Append(child.Model.GroupName);
            first = false;
        }
        if (!first) sb.AppendLine();
        sb.Append(indentStr).AppendLine("        ];");
        foreach (var initializer in accessorInitializers)
        {
            sb.Append(indentStr).Append("        ");
            initializer.Invoke(sb);
        }
        sb.Append(indentStr).AppendLine("    }");

        // CheckScope
        sb.Append(indentStr).AppendLine("    public IEnumerable<string> CheckScope(IReadOnlySet<string> keys)");
        sb.Append(indentStr).AppendLine("    {");
        sb.Append(indentStr).AppendLine("        IEnumerable<string> result = [];");
        sb.Append(indentStr).AppendLine("        foreach (var scope in _InnerScopes)");
        sb.Append(indentStr).AppendLine("        {");
        sb.Append(indentStr).AppendLine("            var next = scope.CheckScope(keys);");
        sb.Append(indentStr).AppendLine("            if (next.Any()) result = result.Concat(next);");
        sb.Append(indentStr).AppendLine("        }");
        sb.Append(indentStr).AppendLine("        return result;");
        sb.Append(indentStr).AppendLine("    }");

        // Reset
        sb.Append(indentStr).AppendLine("    public bool Reset(object? argument = null)");
        sb.Append(indentStr).AppendLine("    {");
        sb.Append(indentStr).AppendLine("        var result = true;");
        sb.Append(indentStr).AppendLine("        foreach (var scope in _InnerScopes)");
        sb.Append(indentStr).AppendLine("        {");
        sb.Append(indentStr).AppendLine("            var next = scope.Reset(argument);");
        sb.Append(indentStr).AppendLine("            if (!next) result = false;");
        sb.Append(indentStr).AppendLine("        }");
        sb.Append(indentStr).AppendLine("        return result;");
        sb.Append(indentStr).AppendLine("    }");

        // IsDefault
        sb.Append(indentStr).AppendLine("    public bool IsDefault(object? argument = null)");
        sb.Append(indentStr).AppendLine("    {");
        sb.Append(indentStr).AppendLine("        var result = true;");
        sb.Append(indentStr).AppendLine("        foreach (var scope in _InnerScopes)");
        sb.Append(indentStr).AppendLine("        {");
        sb.Append(indentStr).AppendLine("            var next = scope.IsDefault(argument);");
        sb.Append(indentStr).AppendLine("            if (!next) result = false;");
        sb.Append(indentStr).AppendLine("        }");
        sb.Append(indentStr).AppendLine("        return result;");
        sb.Append(indentStr).AppendLine("    }");

        sb.Append(indentStr).AppendLine("}");
    }

    public static string _RenderSourceCode(SemanticModel sm, ExpressionSyntax expr)
    {
        var sym = sm.GetSymbolInfo(expr).Symbol;
        if (sym is IFieldSymbol fs && fs.ContainingType?.ToDisplayString() == "PCL.Core.App.Configuration.ConfigSource")
        {
            return "ConfigSource." + fs.Name;
        }
        return expr.ToString();
    }

    // ===== Models/Trees =====

    private sealed class ItemModel
    {
        public IPropertySymbol Property { get; set; } = null!;
        public string Key { get; set; } = "";
        public ITypeSymbol Type { get; set; } = null!;
        public bool IsStatic { get; set; }
        public int DeclOrder { get; set; }
        public string DefaultValueCode { get; set; } = "";
        public string? SourceCode { get; set; }
    }

    private sealed class GroupModel
    {
        public INamedTypeSymbol GroupType { get; set; } = null!;
        public string GroupName { get; set; } = "";
        public int DeclOrder { get; set; }
        public bool HasDeclaredSource { get; set; }
        public string? DeclaredSourceCode { get; set; }
    }

    private sealed class GroupNode(GroupModel model)
    {
        public GroupModel Model { get; } = model;
        public List<GroupNode> Children { get; } = [];
        public List<ItemModel> Items { get; } = [];
    }

    private sealed class ConfigModel
    {
        public INamedTypeSymbol ConfigType { get; set; } = null!;
        // ReSharper disable once UnusedAutoPropertyAccessor.Local
        public int DeclOrder { get; set; }
    }

    private sealed class ConfigTree
    {
        public string Namespace { get; set; } = "";
        public INamedTypeSymbol ConfigType { get; set; } = null!;
        public List<ItemModel> TopItems { get; set; } = [];
        public List<GroupNode> TopGroups { get; set; } = [];
        public Func<ItemModel, string> ResolveSourceCode { get; set; } = static _ => "ConfigSource.Shared";
    }

    private sealed class EventRegisterModel
    {
        public IPropertySymbol Property { get; set; } = null!;
        public int DeclOrder { get; set; }
    }
}
