using System;
using System.Reflection;

namespace AyCode.Core.Serializers;

public abstract class AcSerializerOptions
{
    public int MaxContextPoolSize { get; init; } = 8;
    public abstract AcSerializerType SerializerType { get; init; }

    /// <summary>
    /// Reference handling mode for circular/shared references.
    /// Default: OnlyId (JSON serializer requires All mode, OnlyId not yet implemented)
    /// Note: Binary serializer supports OnlyId mode for IId-only tracking.
    /// </summary>
    public ReferenceHandlingMode ReferenceHandling
    {
        get => _referenceHandling;
        set => _referenceHandling = value;
    }

    private ReferenceHandlingMode _referenceHandling = ReferenceHandlingMode.OnlyId;

    private readonly byte _maxDepth = byte.MaxValue;
    private readonly bool _throwOnCircularReference = true;
    private readonly PropertyMapperDelegate? _propertyMapper;

    private bool _useAsync = false;

    public bool UseAsync
    {
        get => _useAsync && (ReferenceHandling != ReferenceHandlingMode.None); //|| UseStringIntern)
        init => _useAsync = !DetectedIsWasm && value;
    }

    /// <summary>
    /// Cached platform detection - true if running in WebAssembly/Browser environment.
    /// </summary>
    protected static readonly bool DetectedIsWasm = OperatingSystem.IsBrowser();

    /// <summary>
    /// Maximum depth for serialization/deserialization. At this depth limit the action defined by
    /// <see cref="MaxDepthBehavior"/> is taken (throw, truncate to null, or skip the check).
    /// 0 = no nesting allowed (shallow-copy semantic: root level only — nested values are truncated/throw at first recursion)
    /// 1 = root + first level of nested objects/collections
    /// byte.MaxValue (255) = effectively unlimited
    /// Default: byte.MaxValue
    /// To disable the depth check entirely, set <see cref="MaxDepthBehavior"/>=<see cref="Serializers.MaxDepthBehavior.Disable"/>.
    /// </summary>
    public byte MaxDepth
    {
        get => _maxDepth;
        init => _maxDepth = value;
    }

    /// <summary>
    /// Action taken when serialization recursion depth reaches <see cref="MaxDepth"/>.
    /// Default: <see cref="Serializers.MaxDepthBehavior.Throw"/> (cycle detection / bug surfacing).
    /// Switch to <see cref="Serializers.MaxDepthBehavior.Truncate"/> for intentional shallow serialization
    /// (client → server delta updates), or <see cref="Serializers.MaxDepthBehavior.Disable"/> for
    /// max-perf when the graph is known cycle-free.
    /// </summary>
    public MaxDepthBehavior MaxDepthBehavior { get; init; } = MaxDepthBehavior.Throw;

    /// <summary>
    /// Throw exception on circular reference detection for non-IId types.
    /// When true: Tracks all objects and throws InvalidOperationException on circular references.
    /// When false: No tracking for non-IId types (faster, but circular refs may cause MaxDepth truncation).
    /// Default: true (production safety)
    /// Note: IId types are always tracked when ReferenceHandling != None.
    /// </summary>
    public bool ThrowOnCircularReference
    {
        get => _throwOnCircularReference;
        init => _throwOnCircularReference = value;
    }

    /// <summary>
    /// Optional callback for custom property mapping during cross-type operations.
    /// Used when deserializing/populating with Deserialize&lt;TSource, TDest&gt; or Populate&lt;TSource, TDest&gt;.
    ///
    /// Use cases:
    /// - Mapping between external DTOs and internal models (different class hierarchies)
    /// - Handling property renames across versions
    /// - Custom property pairing logic
    ///
    /// If null (default), properties are matched by name.
    /// Callback is invoked once during mapping build phase and result is cached.
    ///
    /// Performance: ZERO overhead on same-type operations (Deserialize&lt;T&gt;).
    /// </summary>
    public PropertyMapperDelegate? PropertyMapper
    {
        get => _propertyMapper;
        init => _propertyMapper = value;
    }
}

public enum AcSerializerType : byte
{
    Json = 0,
    Binary = 1,
    Toon = 2,
}

/// <summary>
/// Behavior when serialization recursion depth reaches <see cref="AcSerializerOptions.MaxDepth"/>.
/// Controls whether the over-depth slice of the object graph is silently truncated, surfaces as
/// an exception, or whether the entire depth check is skipped for maximum throughput.
/// </summary>
public enum MaxDepthBehavior : byte
{
    /// <summary>
    /// Throw <see cref="System.InvalidOperationException"/> when depth limit is reached — cycle
    /// detection and bug surfacing. Recommended default: an over-depth payload is almost always a bug
    /// (circular reference without proper <see cref="ReferenceHandlingMode"/> tracking, or
    /// pathologically deep graph). The exception message reports the offending type and depth so the
    /// failure mode is debuggable rather than silent.
    /// </summary>
    Throw = 0,

    /// <summary>
    /// Replace the over-depth value with a <c>Null</c> marker in the wire stream — intentional shallow
    /// serialization. Use when sending partial graphs (e.g., client → server delta updates, view-model
    /// projections) where deep references are intentionally cut. The receiver reads <c>null</c> at the
    /// truncation boundary: at the wire level this is indistinguishable from a genuine <c>null</c>, so
    /// the consumer must encode that semantic in its protocol (e.g., "only modified properties are
    /// persisted; nested nulls are skipped, not overwritten").
    /// </summary>
    Truncate = 1,

    /// <summary>
    /// Skip the depth limit check entirely — maximum hot-path performance. Use only when the developer
    /// guarantees the object graph is cycle-free and structurally bounded; otherwise the serializer
    /// can stack-overflow on pathological input. Equivalent to setting <see cref="AcSerializerOptions.MaxDepth"/>=0
    /// but expresses intent at the option level instead of via a magic depth value.
    /// </summary>
    Disable = 2,
}

/// <summary>
/// Reference handling mode for serialization.
/// </summary>
public enum ReferenceHandlingMode : byte
{
    /// <summary>
    /// No reference handling - all objects serialized inline.
    /// </summary>
    None = 0,
    
    /// <summary>
    /// Reference handling only for IId objects - uses semantic Id for deduplication.
    /// NOTE: Not fully implemented for JSON serializer - use All instead.
    /// Binary serializer supports this mode.
    /// </summary>
    OnlyId = 1,
    
    /// <summary>
    /// Full reference handling for all objects.
    /// </summary>
    All = 2
}

/// <summary>
/// Wire encoding mode. Pick by content + host:
/// ASCII-heavy → <see cref="Compact"/> (smaller wire, faster CPU).
/// UTF-8 multi-byte → <see cref="Fast"/> (larger wire, faster CPU).
/// AVX-512BW host → <see cref="Compact"/> wins everywhere (smaller wire on all content, CPU parity with Fast).
/// </summary>
public enum WireMode : byte
{
    /// <summary>
    /// VarInt + UTF-8 with ASCII marker-dispatch fast-path. Smallest wire on all content types.
    /// Best for ASCII-heavy workloads, any workload where wire size matters (network, storage),
    /// or any workload on AVX-512BW hosts.
    /// </summary>
    Compact = 0,

    /// <summary>
    /// Fixed-width ints + UTF-16 raw memcpy (no UTF-8 transcoding). Larger wire (2 bytes/char fixed).
    /// Best for UTF-8 multi-byte workloads (i18n / CJK) on non-AVX-512 hosts where wire size
    /// matters less than CPU throughput (in-memory IPC, fast local transport).
    /// </summary>
    Fast = 1
}

/// <summary>
/// Delegate for custom property mapping during cross-type deserialization/population.
/// Enables mapping between different class hierarchies or renamed properties.
/// </summary>
/// <param name="sourceProperty">Property from the source type being deserialized</param>
/// <param name="destinationType">Target type being populated</param>
/// <returns>Mapped destination property, or null to skip this property</returns>
public delegate PropertyInfo? PropertyMapperDelegate(PropertyInfo sourceProperty, Type destinationType);