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

namespace AyCode.Core.Serializers.SourceGenerator;

/// <summary>
/// Incremental source generator for <c>[AcBinarySerializable]</c> types. Emits an
/// <c>IGeneratedBinaryWriter</c> + <c>IGeneratedBinaryReader</c> implementation for every annotated
/// type, plus a <c>[ModuleInitializer]</c>-based registry hook that wires the generated instances
/// into the runtime serializer at startup.
///
/// <para><b>Source organization</b> — this generator class is split across multiple partial files
/// for navigational clarity:</para>
/// <list type="bullet">
///   <item><c>AcBinarySourceGenerator.cs</c> (this file) — entry point: <c>[Generator]</c> attribute,
///   <c>Initialize</c> + <c>Execute</c> orchestration.</item>
///   <item><c>AcBinarySourceGenerator.Models.cs</c> — non-partial model types
///   (<c>SerializableClassInfo</c>, <c>PropInfo</c>, <c>PropertyTypeKind</c>).</item>
///   <item><c>AcBinarySourceGenerator.TypeAnalysis.cs</c> — Roslyn-symbol utility passes
///   (kind detection, FNV hashing, name flattening, scan-need recursion).</item>
///   <item><c>AcBinarySourceGenerator.Diagnostics.cs</c> — ACBIN001 (cycle warning) + ACBIN002
///   (polymorph misuse error) descriptors and detection methods.</item>
///   <item><c>AcBinarySourceGenerator.GetClassInfo.cs</c> — class-info extraction pass (attribute
///   flags + property metadata building).</item>
///   <item><c>AcBinarySourceGenerator.GenWriter.cs</c> — writer-side emit (WriteProperties, ScanObject,
///   ScanForDuplicates, per-property emit helpers).</item>
///   <item><c>AcBinarySourceGenerator.GenReader.cs</c> — reader-side emit (ReadProperties, ReadObject,
///   per-property read helpers).</item>
///   <item><c>AcBinarySourceGenerator.GenInit.cs</c> — ModuleInitializer-based registry hook emit.</item>
/// </list>
/// </summary>
[Generator]
public partial class AcBinarySourceGenerator : IIncrementalGenerator
{
    private const string AttributeName = "AyCode.Core.Serializers.Attributes.AcBinarySerializableAttribute";

    // Feature gates on the SGen-emitted writer / scan code are driven by `[AcBinarySerializable]`
    // attribute flags. Two such gates are wired through SerializableClassInfo:
    //   • EnablePropertyFilter   → omits the per-property `HasPropertyFilter` branch when false.
    //   • EnablePolymorphDetect  → omits the `ObjectWithTypeName + AQN` prefix on `System.Object`-
    //                              declared properties when false (then ACBIN002 guards misuse).
    //   • EnableInternString     → omits StringInterned* case-emit in the reader switch when false.
    // All default `true`; opt-out is per type via the attribute ctor parameters.

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var classDeclarations = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                AttributeName,
                predicate: static (node, _) => node is ClassDeclarationSyntax || node is StructDeclarationSyntax,
                transform: static (ctx, _) => GetClassInfo(ctx))
            .Where(static info => info != null);

        context.RegisterSourceOutput(classDeclarations.Collect(),
            static (spc, classes) => Execute(classes!, spc));
    }

    private static void Execute(ImmutableArray<SerializableClassInfo?> classes, SourceProductionContext context)
    {
        if (classes.IsDefaultOrEmpty) return;
        var valid = classes.Where(c => c != null).Cast<SerializableClassInfo>().ToList();
        if (valid.Count == 0) return;

        DetectAndReportCycles(valid, context);
        DetectAndReportPolymorphicMisuse(valid, context);

        foreach (var ci in valid)
        {
            context.AddSource($"{ci.ClassName}_GeneratedWriter.g.cs", SourceText.From(GenWriter(ci), Encoding.UTF8));
            context.AddSource($"{ci.ClassName}_GeneratedReader.g.cs", SourceText.From(GenReader(ci), Encoding.UTF8));
        }

        context.AddSource("AcBinaryGeneratedWriters_Init.g.cs", SourceText.From(GenInit(valid), Encoding.UTF8));
    }
}
