using System.Runtime.CompilerServices;
using AyCode.Core.Compression;

namespace AyCode.Core.Serializers.Binaries;

/// <summary>
/// Options for AcBinarySerializer and AcBinaryDeserializer.
/// Optimized for speed and memory efficiency over raw size.
/// </summary>
public sealed class AcBinarySerializerOptions : AcSerializerOptions
{
    public override AcSerializerType SerializerType { get; init; } = AcSerializerType.Binary;

    /// <summary>
    /// Current binary format version. Incremented when breaking changes are made.
    /// </summary>
    public const byte FormatVersion = 1;

    /// <summary>
    /// Default options instance with metadata and string interning enabled.
    /// Returns a new instance each time to prevent shared state corruption.
    /// </summary>
    public static AcBinarySerializerOptions Default => new();

    /// <summary>
    /// Options optimized for maximum speed (no interning, no references).
    /// Use when deserializer knows the exact type structure.
    /// Returns a new instance each time to prevent shared state corruption.
    /// </summary>
    public static AcBinarySerializerOptions FastMode => new()
    {
        UseStringInterning = StringInterningMode.None,
        ReferenceHandling = ReferenceHandlingMode.None
    };

    /// <summary>
    /// Options for flat serialization (root level only).
    /// Returns a new instance each time to prevent shared state corruption.
    /// </summary>
    public static AcBinarySerializerOptions FlatCopy => new()
    {
        MaxDepth = 0,
        // Truncate preserves the original "root + Null nested" semantic; under default Throw the preset
        // would throw on the first nested object, defeating its purpose.
        MaxDepthBehavior = MaxDepthBehavior.Truncate,
        UseStringInterning = StringInterningMode.None,
        ReferenceHandling = ReferenceHandlingMode.None
    };

    /// <summary>
    /// Options optimized for WASM environment with string caching enabled.
    /// Returns a new instance each time to prevent shared state corruption.
    /// </summary>
    public static AcBinarySerializerOptions WasmOptimized => new()
    {
        IsWasm = true,
        UseStringCaching = true
    };

    /// <summary>
    /// Whether running in WebAssembly/Browser environment.
    /// When true, enables WASM-specific optimizations like string caching.
    /// Default: auto-detected via OperatingSystem.IsBrowser()
    /// </summary>
    public bool IsWasm { get; init; } = DetectedIsWasm;

    /// <summary>
    /// Whether to cache short strings during deserialization to reduce allocations.
    /// Most beneficial in WASM where GC is expensive.
    /// Auto-enabled when IsWasm is true, can be overridden.
    /// Default: follows IsWasm setting
    /// </summary>
    public bool UseStringCaching { get; private init; } = DetectedIsWasm;

    /// <summary>
    /// Maximum string length to cache when UseStringCaching is enabled.
    /// Longer strings are not cached to avoid memory bloat.
    /// Default: 64 characters
    /// </summary>
    public int MaxCachedStringLength { get; init; } = 64;

    /// <summary>
    /// Whether to include property hash metadata in footer for cross-type deserialization.
    /// When enabled, property name hashes (FNV-1a) are written per type in the footer,
    /// allowing the deserializer to match properties by name between different types.
    /// Default: false (no overhead)
    /// </summary>
    public bool UseMetadata { get; set; } = false;
    public bool UseGeneratedCode { get; set; } = true;

    /// <summary>
    /// Wire encoding mode.
    /// Compact: VarInt + UTF-8 (default, smaller output).
    /// Fast: Fixed-width integers + UTF-16 (larger output, faster encode/decode).
    /// </summary>
    public WireMode WireMode { get; set; } = WireMode.Compact;

    /// <summary>
    /// Controls how string interning is applied during serialization.
    /// None: No interning, all strings written inline.
    /// Attribute: Only properties with [AcStringIntern(true)] are interned.
    /// All: All strings within length limits are interned (legacy behavior).
    /// Default: All
    /// </summary>
    public StringInterningMode UseStringInterning { get; set; } = StringInterningMode.All;

    /// <summary>
    /// When true, checks for duplicate property name hashes during serialization (UseMetadata mode).
    /// Throws exception if FNV-1a hash collision is detected between property names of the same type.
    /// Should be enabled during development/testing, can be disabled in production for performance.
    /// Default: true (safety first)
    /// </summary>
    public bool CheckDuplicatePropName { get; init; } = true;

    /// <summary>
    /// Minimum string length to consider for interning.
    /// Shorter strings are written inline to avoid overhead.
    /// Default: 4 (strings shorter than 4 chars are not interned)
    /// </summary>
    public byte MinStringInternLength { get; init; } = 4;

    /// <summary>
    /// Maximum string length to consider for interning.
    /// Longer strings (descriptions, notes, etc.) are usually unique and not worth interning.
    /// Set to 0 to disable max limit.
    /// Default: 64 (strings longer than 64 chars are not interned)
    /// </summary>
    public byte MaxStringInternLength { get; init; } = 64;

    /// <summary>
    /// Initial capacity for serialization buffer.
    /// Default: 16384 bytes (16 KB)
    /// </summary>
    public int InitialBufferCapacity { get; init; } = 16384;

    /// <summary>
    /// Chunk size (in bytes) used by <see cref="BufferWriterBinaryOutput"/> when writing to an <see cref="System.Buffers.IBufferWriter{T}"/>.
    /// Controls how much data is accumulated before committing (Advance + GetMemory) to the underlying writer.
    ///
    /// <para><b>How it works:</b> The serializer writes into a chunk buffer. When the chunk fills up,
    /// it commits the written bytes to the IBufferWriter and acquires a new chunk. Larger chunks mean
    /// fewer Grow() calls (less overhead), but consume more memory per chunk. Smaller chunks reduce
    /// memory footprint and latency-to-first-byte for streaming, but increase Grow() call frequency.</para>
    ///
    /// <para><b>Choosing a value:</b></para>
    /// <list type="bullet">
    ///   <item><b>Memory-backed writers</b> (ArrayPooledBufferWriter, file/DB blob): use 65536 (64 KB, the default).
    ///   Stays below the .NET LOH threshold (85 KB), minimizes Grow() overhead for large payloads.
    ///   An 8 MB payload triggers ~128 Grow() calls.</item>
    ///   <item><b>Network streaming</b> (Kestrel PipeWriter, SignalR): use 4096 (4 KB).
    ///   Aligns with Kestrel's default memory pool slab size and TCP segment sizes (~1500 byte MTU × 3).
    ///   Reduces latency-to-first-byte by flushing data to the wire sooner.</item>
    /// </list>
    ///
    /// <para><b>Impact of wrong value:</b> Using 64 KB on a network stream adds minor latency for the first chunk.
    /// Using 4 KB for a memory-backed writer causes ~16× more Grow() calls than necessary (2048 vs 128 for 8 MB).
    /// The default (64 KB) is the safe choice — suboptimal on network streams but never catastrophic.</para>
    ///
    /// Default: 65535 (UINT16 max — wire-format invariant for AsyncSegment chunked framing).
    /// </summary>
    public int BufferWriterChunkSize { get; set; } = 65535;

    /// <summary>
    /// Optional property-level filter invoked before metadata registration and serialization.
    /// Return false to exclude the property from the payload.
    /// </summary>
    public BinaryPropertyFilter? PropertyFilter { get; init; }

    /// <summary>
    /// When true, PopulateMerge will remove items from destination collections
    /// that have no matching Id in the source data.
    /// Only applies to IId collections during merge operations.
    /// Default: false (orphaned items are kept)
    /// </summary>
    public bool RemoveOrphanedItems { get; init; } = false;

    /// <summary>
    /// Controls LZ4 compression for serialized data.
    /// None: No compression (default, fastest).
    /// Block: Compresses entire payload as single block (better compression ratio).
    /// BlockArray: Compresses in 64KB chunks (streaming-friendly, lower memory).
    /// Note: Both modes are WASM-compatible (pure managed implementation).
    /// Default: None
    /// </summary>
    public Lz4CompressionMode UseCompression { get; set; } = Lz4CompressionMode.None;

    /// <summary>
    /// Creates options with specified max depth.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static AcBinarySerializerOptions WithMaxDepth(byte maxDepth) => new() { MaxDepth = maxDepth };

    /// <summary>
    /// Creates options without reference handling (and string interning disabled for speed).
    /// </summary>
    public static AcBinarySerializerOptions WithoutReferenceHandling => new() 
    { 
        ReferenceHandling = ReferenceHandlingMode.None,
    };

    /// <summary>
    /// Creates options without metadata (faster but less flexible).
    /// </summary>
    public static AcBinarySerializerOptions WithoutMetadata => new() { UseMetadata = false };
}