using ZB.MOM.WW.CBDD.Core.Indexing;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Shared;
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;

namespace ZB.MOM.WW.CBDD.Tests;

public class CollectionIndexManagerAndDefinitionTests
{
    /// <summary>
    /// Tests find best index should prefer unique index.
    /// </summary>
    [Fact]
    public void FindBestIndex_Should_Prefer_Unique_Index()
    {
        var dbPath = NewDbPath();
        try
        {
            using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
            var mapper = new ZB_MOM_WW_CBDD_Shared_PersonMapper();
            using var manager = new CollectionIndexManager<int, Person>(storage, mapper, "people_idx_pref_unique");

            manager.CreateIndex(p => p.Age, name: "idx_age", unique: false);
            manager.CreateIndex(p => p.Age, name: "idx_age_unique", unique: true);

            var best = manager.FindBestIndex("Age");

            best.ShouldNotBeNull();
            best.Definition.Name.ShouldBe("idx_age_unique");
            best.Definition.IsUnique.ShouldBeTrue();
        }
        finally
        {
            CleanupFiles(dbPath);
        }
    }

    /// <summary>
    /// Tests find best compound index should choose longest prefix.
    /// </summary>
    [Fact]
    public void FindBestCompoundIndex_Should_Choose_Longest_Prefix()
    {
        var dbPath = NewDbPath();
        try
        {
            using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
            var mapper = new ZB_MOM_WW_CBDD_Shared_PersonMapper();
            using var manager = new CollectionIndexManager<int, Person>(storage, mapper, "people_idx_compound");

            manager.CreateIndex(new CollectionIndexDefinition<Person>(
                "idx_name",
                ["Name"],
                p => p.Name));

            manager.CreateIndex(new CollectionIndexDefinition<Person>(
                "idx_name_age",
                ["Name", "Age"],
                p => new { p.Name, p.Age }));

            manager.CreateIndex(new CollectionIndexDefinition<Person>(
                "idx_name_age_id",
                ["Name", "Age", "Id"],
                p => new { p.Name, p.Age, p.Id }));

            var best = manager.FindBestCompoundIndex(["Name", "Age"]);

            best.ShouldNotBeNull();
            best.Definition.Name.ShouldBe("idx_name_age_id");
            best.Definition.PropertyPaths.Length.ShouldBe(3);
        }
        finally
        {
            CleanupFiles(dbPath);
        }
    }

    /// <summary>
    /// Tests drop index should remove metadata and be idempotent.
    /// </summary>
    [Fact]
    public void DropIndex_Should_Remove_Metadata_And_Be_Idempotent()
    {
        var dbPath = NewDbPath();
        const string collectionName = "people_idx_drop";

        try
        {
            using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
            var mapper = new ZB_MOM_WW_CBDD_Shared_PersonMapper();

            using (var manager = new CollectionIndexManager<int, Person>(storage, mapper, collectionName))
            {
                manager.CreateIndex(p => p.Age, name: "idx_age", unique: false);
                manager.DropIndex("idx_age").ShouldBeTrue();
                manager.DropIndex("idx_age").ShouldBeFalse();
                manager.GetIndexInfo().ShouldBeEmpty();
            }

            using var reloaded = new CollectionIndexManager<int, Person>(storage, mapper, collectionName);
            reloaded.GetIndexInfo().ShouldBeEmpty();
        }
        finally
        {
            CleanupFiles(dbPath);
        }
    }

    /// <summary>
    /// Tests collection index definition should respect query support rules.
    /// </summary>
    [Fact]
    public void CollectionIndexDefinition_Should_Respect_Query_Support_Rules()
    {
        var definition = new CollectionIndexDefinition<Person>(
            "idx_name_age",
            ["Name", "Age"],
            p => new { p.Name, p.Age });

        definition.CanSupportQuery("Name").ShouldBeTrue();
        definition.CanSupportQuery("Age").ShouldBeFalse();

        definition.CanSupportCompoundQuery(["Name"]).ShouldBeTrue();
        definition.CanSupportCompoundQuery(["Name", "Age"]).ShouldBeTrue();
        definition.CanSupportCompoundQuery(["Name", "Age", "Id"]).ShouldBeFalse();

        definition.ToString().ShouldContain("idx_name_age");
        definition.ToString().ShouldContain("Name");
    }

    /// <summary>
    /// Tests collection index info to string should include diagnostics.
    /// </summary>
    [Fact]
    public void CollectionIndexInfo_ToString_Should_Include_Diagnostics()
    {
        var info = new CollectionIndexInfo
        {
            Name = "idx_age",
            PropertyPaths = ["Age"],
            EstimatedDocumentCount = 12,
            EstimatedSizeBytes = 4096
        };

        var text = info.ToString();

        text.ShouldContain("idx_age");
        text.ShouldContain("Age");
        text.ShouldContain("12 docs");
    }

    private static string NewDbPath()
        => Path.Combine(Path.GetTempPath(), $"idx_mgr_{Guid.NewGuid():N}.db");

    private static void CleanupFiles(string dbPath)
    {
        if (File.Exists(dbPath)) File.Delete(dbPath);

        var walPath = Path.ChangeExtension(dbPath, ".wal");
        if (File.Exists(walPath)) File.Delete(walPath);

        var altWalPath = dbPath + "-wal";
        if (File.Exists(altWalPath)) File.Delete(altWalPath);
    }
}
