using System;

namespace AyCode.Core.Serializers.Attributes;

/// <summary>
/// Marks a class or struct for Source Generator based binary serialization.
/// When applied, the Source Generator creates optimized serialize/deserialize methods at compile
/// time, eliminating the need for runtime reflection or compiled expressions.
/// </summary>
/// <remarks>
/// <para>If this attribute is not present, the serializer falls back to the existing
/// compiled-expression-based approach which works for all types.</para>
///
/// <para><b>Feature flag semantics</b> — each <c>Enable*Feature</c> ctor parameter controls whether
/// the corresponding code-block is <i>emitted</i> by the source generator, NOT whether the feature
/// is <i>active</i> at runtime:</para>
/// <list type="bullet">
///   <item><b><c>true</c> (default):</b> the SGen-emitted writer / reader / scan code <i>includes</i>
///   the feature's branch. Runtime activation is then governed by the matching
///   <c>AcBinarySerializerOptions</c> setting (e.g. <c>UseStringInterning</c>,
///   <c>ReferenceHandling</c>, <c>PropertyFilter</c>, <c>UseMetadata</c>). The developer keeps the
///   choice per-call by setting the options accordingly.</item>
///   <item><b><c>false</c>:</b> the corresponding code-block is <i>omitted</i> from the generated
///   output entirely. Slightly leaner hot-path (fewer branches, less i-cache pressure on the JIT'd
///   bodies), but the feature is <i>permanently off</i> for this type regardless of runtime options.
///   A matching <c>AcBinarySerializerOptions</c> setting is silently ignored for this specific type.</item>
/// </list>
///
/// <para>Set a flag to <c>false</c> only when you can guarantee no consumer will ever need that
/// feature on this type AND the marginal hot-path speedup matters for the workload. Otherwise
/// leave it at the default <c>true</c>.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
public sealed class AcBinarySerializableAttribute : Attribute
{
    public bool EnableMetadataFeature { get; }

    /// <summary>
    /// When <c>true</c> (default): the SGen-emitted code recognizes the type's tracking-Id property
    /// and emits Id-keyed reference deduplication support, active when <c>ReferenceHandling</c> is
    /// <c>OnlyId</c> or <c>All</c>.
    /// <para>When <c>false</c>: Id-tracking emit is skipped entirely. <b>Significantly reduces
    /// scan-pass cost</b> — the per-instance <c>wrapper.TryTrack*</c> call + <c>IdentityMap</c>
    /// lookup is one of the dominant scan-phase costs. Combined with <c>EnableRefHandlingFeature
    /// = false</c>, the scan body for this type collapses to near-no-op. Reader-side
    /// <c>ObjectRefFirst</c> / <c>ObjectRef</c> case-emit also strips when the child subtree's
    /// tracking is provably absent. The runtime <c>ReferenceHandling</c> option is silently
    /// ignored for instances of this type. Use only when this type is never shared by Id (e.g.
    /// immutable value-DTO, single-use message payload, append-only log entry).</para>
    /// </summary>
    public bool EnableIdTrackingFeature { get; }

    /// <summary>
    /// Controls emit of this type's non-IId hash-based reference tracking (self-registration in the
    /// scan pass, paired with runtime <c>ReferenceHandling</c>). Scope: ONLY this type's self-tracking
    /// — does NOT govern marker dispatch for child properties (child marker emit follows the child
    /// type's own facts, symmetric writer/reader). See class remarks for the general flag semantics.
    /// </summary>
    public bool EnableRefHandlingFeature { get; }

    /// <summary>
    /// When <c>true</c> (default): the SGen-emitted code emits string-interning support for
    /// intern-eligible properties — <c>context.ScanInternString(str)</c> in the scan pass and
    /// <c>StringInterned</c> / <c>StringInternFirstSmall</c> / <c>StringInternFirstMedium</c>
    /// case-emit in the reader.
    /// <para>When <c>false</c>: both emit blocks are omitted. <b>Significantly reduces scan-pass
    /// cost</b> — string-property iteration with <c>IdentityMap</c> lookup is eliminated entirely
    /// (often the heaviest scan-phase work on string-rich DTOs). The writer's per-property
    /// <c>StringInternEligible</c> flag is always <c>false</c> for this type, so plan-entry
    /// consumption never fires. Reader switch dispatch shrinks by 3 cases per string property
    /// (smaller jump table, better branch predictor). The runtime <c>UseStringInterning</c> option
    /// is silently ignored for instances of this type. Use only when this type's strings have low
    /// intern-yield (unique per instance — Ids, GUIDs, free-text, never-repeated content).</para>
    /// </summary>
    public bool EnableInternStringFeature { get; }

    /// <summary>
    /// When <c>true</c> (default): the SGen-emitted writer/scan code includes the per-property
    /// <c>HasPropertyFilter</c> branch + filter-context allocation + lambda-call. Required for runtime
    /// <see cref="AyCode.Core.Serializers.Binaries.BinaryPropertyFilter"/> to take effect on this type.
    /// <para>When <c>false</c>: the SGen-emitted writer/scan code <b>omits</b> the filter check entirely
    /// for every property of this type — leaner generated code on the hot path. Use this when the
    /// type is never serialized with a property-filter (typical for high-throughput message types).
    /// Filter callbacks set on <c>AcBinarySerializerOptions.PropertyFilter</c> will be silently ignored
    /// for this type's properties — only set <c>false</c> if you can guarantee no consumer relies on
    /// per-property filtering for this specific type.</para>
    /// </summary>
    public bool EnablePropertyFilterFeature { get; }
    public bool EnablePolymorphDetectFeature { get; }

    public AcBinarySerializableAttribute() : this(true)
    {
    }

    public AcBinarySerializableAttribute(bool enableAllFeatures) : this(enableAllFeatures, enableAllFeatures, enableAllFeatures, enableAllFeatures, enableAllFeatures, enableAllFeatures)
    { }

    public AcBinarySerializableAttribute(bool enableMetadataFeature, bool enableIdTrackingFeature, bool enableRefHandlingFeature, bool enableInternStringFeature, bool enablePropertyFilterFeature, bool enablePolymorphDetectFeature)
    {
        EnableMetadataFeature = enableMetadataFeature;
        EnableIdTrackingFeature = enableIdTrackingFeature;
        EnableRefHandlingFeature = enableRefHandlingFeature;
        EnableInternStringFeature = enableInternStringFeature;
        EnablePropertyFilterFeature = enablePropertyFilterFeature;
        EnablePolymorphDetectFeature = enablePolymorphDetectFeature;
    }
}
