using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Storage;

namespace ZB.MOM.WW.CBDD.Core.Indexing;

/// <summary>
///     Represents an entry in an index mapping a key to a document location.
///     Implemented as struct for memory efficiency.
/// </summary>
public struct IndexEntry : IComparable<IndexEntry>, IComparable
{
    /// <summary>
    ///     Gets or sets the index key.
    /// </summary>
    public IndexKey Key { get; set; }

    /// <summary>
    ///     Gets or sets the document location for the key.
    /// </summary>
    public DocumentLocation Location { get; set; }

    /// <summary>
    ///     Initializes a new instance of the <see cref="IndexEntry" /> struct.
    /// </summary>
    /// <param name="key">The index key.</param>
    /// <param name="location">The document location.</param>
    public IndexEntry(IndexKey key, DocumentLocation location)
    {
        Key = key;
        Location = location;
    }

    // Backward compatibility: constructor that takes ObjectId (for migration)
    // Will be removed once all code is migrated
    /// <summary>
    ///     Initializes a legacy instance of the <see cref="IndexEntry" /> struct for migration scenarios.
    /// </summary>
    /// <param name="key">The index key.</param>
    /// <param name="documentId">The legacy document identifier.</param>
    [Obsolete("Use constructor with DocumentLocation instead")]
    public IndexEntry(IndexKey key, ObjectId documentId)
    {
        Key = key;
        // Create a temporary location (will be replaced by proper implementation)
        Location = new DocumentLocation(0, 0);
    }

    /// <summary>
    ///     Compares this entry to another entry by key.
    /// </summary>
    /// <param name="other">The other index entry to compare.</param>
    /// <returns>
    ///     A value less than zero if this instance is less than <paramref name="other" />,
    ///     zero if they are equal, or greater than zero if this instance is greater.
    /// </returns>
    public int CompareTo(IndexEntry other)
    {
        return Key.CompareTo(other.Key);
    }

    /// <summary>
    ///     Compares this entry to another object.
    /// </summary>
    /// <param name="obj">The object to compare.</param>
    /// <returns>
    ///     A value less than zero if this instance is less than <paramref name="obj" />,
    ///     zero if they are equal, or greater than zero if this instance is greater.
    /// </returns>
    /// <exception cref="ArgumentException">Thrown when <paramref name="obj" /> is not an <see cref="IndexEntry" />.</exception>
    public int CompareTo(object? obj)
    {
        if (obj is IndexEntry other) return CompareTo(other);
        throw new ArgumentException("Object is not an IndexEntry");
    }
}

/// <summary>
///     B+Tree node for index storage.
///     Uses struct for node metadata to minimize allocations.
/// </summary>
public struct BTreeNodeHeader
{
    /// <summary>
    ///     Gets or sets the page identifier.
    /// </summary>
    public uint PageId { get; set; }

    /// <summary>
    ///     Gets or sets a value indicating whether this node is a leaf node.
    /// </summary>
    public bool IsLeaf { get; set; }

    /// <summary>
    ///     Gets or sets the number of entries in the node.
    /// </summary>
    public ushort EntryCount { get; set; }

    /// <summary>
    ///     Gets or sets the parent page identifier.
    /// </summary>
    public uint ParentPageId { get; set; }

    /// <summary>
    ///     Gets or sets the next leaf page identifier.
    /// </summary>
    public uint NextLeafPageId { get; set; } // For leaf nodes only

    /// <summary>
    ///     Gets or sets the previous leaf page identifier.
    /// </summary>
    public uint PrevLeafPageId { get; set; } // For leaf nodes only (added for reverse scan)

    /// <summary>
    ///     Writes the header to a byte span.
    /// </summary>
    /// <param name="destination">The destination span.</param>
    public void WriteTo(Span<byte> destination)
    {
        if (destination.Length < 20)
            throw new ArgumentException("Destination must be at least 20 bytes");

        BitConverter.TryWriteBytes(destination[..4], PageId);
        destination[4] = (byte)(IsLeaf ? 1 : 0);
        BitConverter.TryWriteBytes(destination[5..7], EntryCount);
        BitConverter.TryWriteBytes(destination[7..11], ParentPageId);
        BitConverter.TryWriteBytes(destination[11..15], NextLeafPageId);
        BitConverter.TryWriteBytes(destination[15..19], PrevLeafPageId);
    }

    /// <summary>
    ///     Reads a node header from a byte span.
    /// </summary>
    /// <param name="source">The source span.</param>
    /// <returns>The parsed node header.</returns>
    public static BTreeNodeHeader ReadFrom(ReadOnlySpan<byte> source)
    {
        if (source.Length < 20)
            throw new ArgumentException("Source must be at least 16 bytes");

        var header = new BTreeNodeHeader
        {
            PageId = BitConverter.ToUInt32(source[..4]),
            IsLeaf = source[4] != 0,
            EntryCount = BitConverter.ToUInt16(source[5..7]),
            ParentPageId = BitConverter.ToUInt32(source[7..11]),
            NextLeafPageId = BitConverter.ToUInt32(source[11..15])
        };

        if (source.Length >= 20) header.PrevLeafPageId = BitConverter.ToUInt32(source[15..19]);

        return header;
    }
}