using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Bson.Schema;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
using System.Diagnostics.CodeAnalysis;
using ZB.MOM.WW.CBDD.Bson.IO;

namespace ZB.MOM.WW.CBDD.Tests;

public class DictionaryPersistenceTests : IDisposable
{
    private readonly string _dbPath;
    private readonly StorageEngine _storage;

    /// <summary>
    ///     Initializes a new instance of the <see cref="DictionaryPersistenceTests" /> class.
    /// </summary>
    public DictionaryPersistenceTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_dict_{Guid.NewGuid():N}.db");
        _storage = new StorageEngine(_dbPath, PageFileConfig.Default);
    }

    /// <summary>
    ///     Disposes test resources and removes temporary files.
    /// </summary>
    public void Dispose()
    {
        _storage.Dispose();
        if (File.Exists(_dbPath)) File.Delete(_dbPath);
        string walPath = Path.ChangeExtension(_dbPath, ".wal");
        if (File.Exists(walPath)) File.Delete(walPath);
    }

    /// <summary>
    ///     Verifies mapper registration adds all unique dictionary keys.
    /// </summary>
    [Fact]
    public void RegisterMappers_Registers_All_Unique_Keys()
    {
        var mapper1 = new MockMapper("Coll1", "Name", "Age");
        var mapper2 = new MockMapper("Coll2", "Name", "Address", "City");

        _storage.RegisterMappers(new IDocumentMapper[] { mapper1, mapper2 });

        // Verify keys in cache
        _storage.GetOrAddDictionaryEntry("Name").ShouldNotBe((ushort)0);
        _storage.GetOrAddDictionaryEntry("Age").ShouldNotBe((ushort)0);
        _storage.GetOrAddDictionaryEntry("Address").ShouldNotBe((ushort)0);
        _storage.GetOrAddDictionaryEntry("City").ShouldNotBe((ushort)0);

        // Verify they have unique IDs (at least 4 unique IDs for 4 unique keys + internal ones)
        var ids = new HashSet<ushort>
        {
            _storage.GetOrAddDictionaryEntry("Name"),
            _storage.GetOrAddDictionaryEntry("Age"),
            _storage.GetOrAddDictionaryEntry("Address"),
            _storage.GetOrAddDictionaryEntry("City")
        };
        ids.Count.ShouldBe(4);
    }

    /// <summary>
    ///     Verifies dictionary keys persist across storage restarts.
    /// </summary>
    [Fact]
    public void Dictionary_Keys_Persist_Across_Restarts()
    {
        var mapper = new MockMapper("Coll1", "PersistedKey");
        _storage.RegisterMappers(new IDocumentMapper[] { mapper });

        ushort originalId = _storage.GetOrAddDictionaryEntry("PersistedKey");
        originalId.ShouldNotBe((ushort)0);

        _storage.Dispose();

        // Re-open
        using var storage2 = new StorageEngine(_dbPath, PageFileConfig.Default);

        ushort recoveredId = storage2.GetOrAddDictionaryEntry("PersistedKey");
        recoveredId.ShouldBe(originalId);
    }

    /// <summary>
    ///     Verifies nested schema fields are registered as dictionary keys.
    /// </summary>
    [Fact]
    public void RegisterMappers_Handles_Nested_Keys()
    {
        var mapper = new NestedMockMapper();
        _storage.RegisterMappers(new IDocumentMapper[] { mapper });

        _storage.GetOrAddDictionaryEntry("Top").ShouldNotBe((ushort)0);
        _storage.GetOrAddDictionaryEntry("Child").ShouldNotBe((ushort)0);
    }

    [SuppressMessage("ReSharper", "All", Justification = "Test-only stub mapper; members are intentionally not used.")]
    private class MockMapper : DocumentMapperBase<ObjectId, Dictionary<string, object>>
    {
        private readonly string _collectionName;
        private readonly List<string> _keys;

        /// <summary>
        ///     Initializes a new instance of the <see cref="MockMapper" /> class.
        /// </summary>
        /// <param name="name">The collection name.</param>
        /// <param name="keys">The mapper keys.</param>
        public MockMapper(string name, params string[] keys)
        {
            _collectionName = name;
            _keys = keys.ToList();
        }

        /// <inheritdoc />
        public override string CollectionName => _collectionName;

        /// <inheritdoc />
        public override IEnumerable<string> UsedKeys => _keys;

        /// <inheritdoc />
        public override BsonSchema GetSchema()
        {
            return new BsonSchema { Title = _collectionName };
        }

        /// <inheritdoc />
        public override ObjectId GetId(Dictionary<string, object> entity)
        {
            throw new NotImplementedException();
        }

        /// <inheritdoc />
        public override void SetId(Dictionary<string, object> entity, ObjectId id)
        {
            throw new NotImplementedException();
        }

        /// <inheritdoc />
        public override int Serialize(Dictionary<string, object> entity, BsonSpanWriter writer)
        {
            throw new NotImplementedException();
        }

        /// <inheritdoc />
        public override Dictionary<string, object> Deserialize(BsonSpanReader reader)
        {
            throw new NotImplementedException();
        }
    }

    [SuppressMessage("ReSharper", "All", Justification = "Test-only stub mapper; members are intentionally not used.")]
    private class NestedMockMapper : DocumentMapperBase<ObjectId, object>
    {
        /// <inheritdoc />
        public override string CollectionName => "Nested";

        /// <inheritdoc />
        public override BsonSchema GetSchema()
        {
            var schema = new BsonSchema { Title = "Nested" };
            schema.Fields.Add(new BsonField
            {
                Name = "Top",
                Type = BsonType.Document,
                NestedSchema = new BsonSchema
                {
                    Fields = { new BsonField { Name = "Child", Type = BsonType.String } }
                }
            });
            return schema;
        }

        /// <inheritdoc />
        public override ObjectId GetId(object entity)
        {
            throw new NotImplementedException();
        }

        /// <inheritdoc />
        public override void SetId(object entity, ObjectId id)
        {
            throw new NotImplementedException();
        }

        /// <inheritdoc />
        public override int Serialize(object entity, BsonSpanWriter writer)
        {
            throw new NotImplementedException();
        }

        /// <inheritdoc />
        public override object Deserialize(BsonSpanReader reader)
        {
            throw new NotImplementedException();
        }
    }
}
