using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Compression;
using ZB.MOM.WW.CBDD.Core.Indexing;
using ZB.MOM.WW.CBDD.Core.Metadata;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;

namespace ZB.MOM.WW.CBDD.Shared;

/// <summary>
///     Test context with manual collection initialization
///     (Source Generator will automate this in the future)
/// </summary>
public partial class TestDbContext : DocumentDbContext
{
    /// <summary>
    ///     Initializes a new instance of the <see cref="TestDbContext" /> class.
    /// </summary>
    /// <param name="databasePath">The database path.</param>
    public TestDbContext(string databasePath)
        : this(databasePath, PageFileConfig.Default, CompressionOptions.Default)
    {
    }

    /// <summary>
    ///     Initializes a new instance of the <see cref="TestDbContext" /> class.
    /// </summary>
    /// <param name="databasePath">The database path.</param>
    /// <param name="compressionOptions">The compression options.</param>
    public TestDbContext(string databasePath, CompressionOptions compressionOptions)
        : this(databasePath, PageFileConfig.Default, compressionOptions)
    {
    }

    /// <summary>
    ///     Initializes a new instance of the <see cref="TestDbContext" /> class.
    /// </summary>
    /// <param name="databasePath">The database path.</param>
    /// <param name="pageFileConfig">The page file configuration.</param>
    public TestDbContext(string databasePath, PageFileConfig pageFileConfig)
        : this(databasePath, pageFileConfig, CompressionOptions.Default)
    {
    }

    /// <summary>
    ///     Initializes a new instance of the <see cref="TestDbContext" /> class.
    /// </summary>
    /// <param name="databasePath">The database path.</param>
    /// <param name="pageFileConfig">The page file configuration.</param>
    /// <param name="compressionOptions">The compression options.</param>
    /// <param name="maintenanceOptions">The maintenance options.</param>
    public TestDbContext(
        string databasePath,
        PageFileConfig pageFileConfig,
        CompressionOptions? compressionOptions,
        MaintenanceOptions? maintenanceOptions = null)
        : base(databasePath, pageFileConfig, compressionOptions, maintenanceOptions)
    {
    }

    /// <summary>
    ///     Gets or sets the AnnotatedUsers.
    /// </summary>
    public DocumentCollection<ObjectId, AnnotatedUser> AnnotatedUsers { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the Orders.
    /// </summary>
    public DocumentCollection<OrderId, Order> Orders { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the TestDocuments.
    /// </summary>
    public DocumentCollection<ObjectId, TestDocument> TestDocuments { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the OrderDocuments.
    /// </summary>
    public DocumentCollection<ObjectId, OrderDocument> OrderDocuments { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the ComplexDocuments.
    /// </summary>
    public DocumentCollection<ObjectId, ComplexDocument> ComplexDocuments { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the Users.
    /// </summary>
    public DocumentCollection<ObjectId, User> Users { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the ComplexUsers.
    /// </summary>
    public DocumentCollection<ObjectId, ComplexUser> ComplexUsers { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the AutoInitEntities.
    /// </summary>
    public DocumentCollection<int, AutoInitEntity> AutoInitEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the People.
    /// </summary>
    public DocumentCollection<int, Person> People { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the PeopleV2.
    /// </summary>
    public DocumentCollection<ObjectId, PersonV2> PeopleV2 { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the Products.
    /// </summary>
    public DocumentCollection<int, Product> Products { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the IntEntities.
    /// </summary>
    public DocumentCollection<int, IntEntity> IntEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the StringEntities.
    /// </summary>
    public DocumentCollection<string, StringEntity> StringEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the GuidEntities.
    /// </summary>
    public DocumentCollection<Guid, GuidEntity> GuidEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the CustomKeyEntities.
    /// </summary>
    public DocumentCollection<string, CustomKeyEntity> CustomKeyEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the AsyncDocs.
    /// </summary>
    public DocumentCollection<int, AsyncDoc> AsyncDocs { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the SchemaUsers.
    /// </summary>
    public DocumentCollection<int, SchemaUser> SchemaUsers { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the VectorItems.
    /// </summary>
    public DocumentCollection<ObjectId, VectorEntity> VectorItems { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the GeoItems.
    /// </summary>
    public DocumentCollection<ObjectId, GeoEntity> GeoItems { get; set; } = null!;

    // Source Generator Feature Tests
    /// <summary>
    ///     Gets or sets the DerivedEntities.
    /// </summary>
    public DocumentCollection<ObjectId, DerivedEntity> DerivedEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the ComputedPropertyEntities.
    /// </summary>
    public DocumentCollection<ObjectId, EntityWithComputedProperties> ComputedPropertyEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the AdvancedCollectionEntities.
    /// </summary>
    public DocumentCollection<ObjectId, EntityWithAdvancedCollections> AdvancedCollectionEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the PrivateSetterEntities.
    /// </summary>
    public DocumentCollection<ObjectId, EntityWithPrivateSetters> PrivateSetterEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the InitSetterEntities.
    /// </summary>
    public DocumentCollection<ObjectId, EntityWithInitSetters> InitSetterEntities { get; set; } = null!;

    // Circular Reference Tests
    /// <summary>
    ///     Gets or sets the Employees.
    /// </summary>
    public DocumentCollection<ObjectId, Employee> Employees { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the CategoryRefs.
    /// </summary>
    public DocumentCollection<ObjectId, CategoryRef> CategoryRefs { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the ProductRefs.
    /// </summary>
    public DocumentCollection<ObjectId, ProductRef> ProductRefs { get; set; } = null!;

    // Nullable String Id Test (UuidEntity scenario with inheritance)
    /// <summary>
    ///     Gets or sets the MockCounters.
    /// </summary>
    public DocumentCollection<string, MockCounter> MockCounters { get; set; } = null!;

    // Temporal Types Test (DateTimeOffset, TimeSpan, DateOnly, TimeOnly)
    /// <summary>
    ///     Gets or sets the TemporalEntities.
    /// </summary>
    public DocumentCollection<ObjectId, TemporalEntity> TemporalEntities { get; set; } = null!;

    /// <summary>
    ///     Gets or sets the Storage.
    /// </summary>
    public StorageEngine Storage => Engine;

    /// <inheritdoc />
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AnnotatedUser>();
        modelBuilder.Entity<User>().ToCollection("users");
        modelBuilder.Entity<ComplexUser>().ToCollection("complex_users");
        modelBuilder.Entity<AutoInitEntity>().ToCollection("auto_init_entities");
        modelBuilder.Entity<Person>().ToCollection("people_collection").HasIndex(p => p.Age);
        modelBuilder.Entity<PersonV2>().ToCollection("peoplev2_collection").HasIndex(p => p.Age);
        modelBuilder.Entity<Product>().ToCollection("products_collection").HasIndex(p => p.Price);
        modelBuilder.Entity<IntEntity>().HasKey(e => e.Id);
        modelBuilder.Entity<StringEntity>().HasKey(e => e.Id);
        modelBuilder.Entity<GuidEntity>().HasKey(e => e.Id);
        modelBuilder.Entity<CustomKeyEntity>().HasKey(e => e.Code);
        modelBuilder.Entity<AsyncDoc>().ToCollection("async_docs");
        modelBuilder.Entity<SchemaUser>().ToCollection("schema_users").HasKey(e => e.Id);
        modelBuilder.Entity<TestDocument>();
        modelBuilder.Entity<OrderDocument>();
        modelBuilder.Entity<ComplexDocument>();

        modelBuilder.Entity<VectorEntity>()
            .ToCollection("vector_items")
            .HasVectorIndex(x => x.Embedding, 3, VectorMetric.L2, "idx_vector");

        modelBuilder.Entity<GeoEntity>()
            .ToCollection("geo_items")
            .HasSpatialIndex(x => x.Location, "idx_spatial");

        modelBuilder.Entity<Order>()
            .HasKey(x => x.Id)
            .HasConversion<OrderIdConverter>();

        // Source Generator Feature Tests
        modelBuilder.Entity<DerivedEntity>().ToCollection("derived_entities");
        modelBuilder.Entity<EntityWithComputedProperties>().ToCollection("computed_property_entities");
        modelBuilder.Entity<EntityWithAdvancedCollections>().ToCollection("advanced_collection_entities");
        modelBuilder.Entity<EntityWithPrivateSetters>().ToCollection("private_setter_entities");
        modelBuilder.Entity<EntityWithInitSetters>().ToCollection("init_setter_entities");

        // Circular Reference Tests
        modelBuilder.Entity<Employee>().ToCollection("employees");
        modelBuilder.Entity<CategoryRef>().ToCollection("category_refs");
        modelBuilder.Entity<ProductRef>().ToCollection("product_refs");

        // Nullable String Id Test (UuidEntity scenario)
        modelBuilder.Entity<MockCounter>().ToCollection("mock_counters").HasKey(e => e.Id);

        // Temporal Types Test
        modelBuilder.Entity<TemporalEntity>().ToCollection("temporal_entities").HasKey(e => e.Id);
    }

    /// <summary>
    ///     Executes ForceCheckpoint.
    /// </summary>
    public void ForceCheckpoint()
    {
        Engine.Checkpoint();
    }

    /// <summary>
    ///     Executes ForceCheckpoint with the requested checkpoint mode.
    /// </summary>
    /// <param name="mode">Checkpoint mode to execute.</param>
    public CheckpointResult ForceCheckpoint(CheckpointMode mode)
    {
        return Engine.Checkpoint(mode);
    }
}