using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Bson.Schema;
using ZB.MOM.WW.CBDD.Core.Indexing;

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

/// <summary>
///     Base class for custom mappers that provides bidirectional IndexKey mapping for standard types.
/// </summary>
public abstract class DocumentMapperBase<TId, T> : IDocumentMapper<TId, T> where T : class
{
    /// <summary>
    ///     Gets the target collection name for the mapped entity type.
    /// </summary>
    public abstract string CollectionName { get; }

    /// <summary>
    ///     Serializes an entity instance into BSON.
    /// </summary>
    /// <param name="entity">The entity to serialize.</param>
    /// <param name="writer">The BSON writer to write into.</param>
    /// <returns>The number of bytes written.</returns>
    public abstract int Serialize(T entity, BsonSpanWriter writer);

    /// <summary>
    ///     Deserializes an entity instance from BSON.
    /// </summary>
    /// <param name="reader">The BSON reader to read from.</param>
    /// <returns>The deserialized entity.</returns>
    public abstract T Deserialize(BsonSpanReader reader);

    /// <summary>
    ///     Gets the identifier value from an entity.
    /// </summary>
    /// <param name="entity">The entity to read the identifier from.</param>
    /// <returns>The identifier value.</returns>
    public abstract TId GetId(T entity);

    /// <summary>
    ///     Sets the identifier value on an entity.
    /// </summary>
    /// <param name="entity">The entity to update.</param>
    /// <param name="id">The identifier value to assign.</param>
    public abstract void SetId(T entity, TId id);

    /// <summary>
    ///     Converts a typed identifier value into an index key.
    /// </summary>
    /// <param name="id">The identifier value.</param>
    /// <returns>The index key representation of the identifier.</returns>
    public virtual IndexKey ToIndexKey(TId id)
    {
        return IndexKey.Create(id);
    }

    /// <summary>
    ///     Converts an index key back into a typed identifier value.
    /// </summary>
    /// <param name="key">The index key to convert.</param>
    /// <returns>The typed identifier value.</returns>
    public virtual TId FromIndexKey(IndexKey key)
    {
        return key.As<TId>();
    }

    /// <summary>
    ///     Gets all mapped field keys used by this mapper.
    /// </summary>
    public virtual IEnumerable<string> UsedKeys => GetSchema().GetAllKeys();

    /// <summary>
    ///     Builds the BSON schema for the mapped entity type.
    /// </summary>
    /// <returns>The generated BSON schema.</returns>
    public virtual BsonSchema GetSchema()
    {
        return BsonSchemaGenerator.FromType<T>();
    }
}

/// <summary>
///     Base class for mappers using ObjectId as primary key.
/// </summary>
public abstract class ObjectIdMapperBase<T> : DocumentMapperBase<ObjectId, T>, IDocumentMapper<T> where T : class
{
    /// <inheritdoc />
    public override IndexKey ToIndexKey(ObjectId id)
    {
        return IndexKey.Create(id);
    }

    /// <inheritdoc />
    public override ObjectId FromIndexKey(IndexKey key)
    {
        return key.As<ObjectId>();
    }
}

/// <summary>
///     Base class for mappers using Int32 as primary key.
/// </summary>
public abstract class Int32MapperBase<T> : DocumentMapperBase<int, T> where T : class
{
    /// <inheritdoc />
    public override IndexKey ToIndexKey(int id)
    {
        return IndexKey.Create(id);
    }

    /// <inheritdoc />
    public override int FromIndexKey(IndexKey key)
    {
        return key.As<int>();
    }
}

/// <summary>
///     Base class for mappers using String as primary key.
/// </summary>
public abstract class StringMapperBase<T> : DocumentMapperBase<string, T> where T : class
{
    /// <inheritdoc />
    public override IndexKey ToIndexKey(string id)
    {
        return IndexKey.Create(id);
    }

    /// <inheritdoc />
    public override string FromIndexKey(IndexKey key)
    {
        return key.As<string>();
    }
}

/// <summary>
///     Base class for mappers using Guid as primary key.
/// </summary>
public abstract class GuidMapperBase<T> : DocumentMapperBase<Guid, T> where T : class
{
    /// <inheritdoc />
    public override IndexKey ToIndexKey(Guid id)
    {
        return IndexKey.Create(id);
    }

    /// <inheritdoc />
    public override Guid FromIndexKey(IndexKey key)
    {
        return key.As<Guid>();
    }
}