using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
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.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;
using System.IO;

namespace ZB.MOM.WW.CBDD.Tests.Benchmark;

[SimpleJob]
[InProcess]
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[HtmlExporter]
[JsonExporterAttribute.Full]
public class ReadBenchmarks
{
    private const int DocCount = 1000;

    private string _docDbPath = null!;
    private string _docDbWalPath = null!;

    private StorageEngine _storage = null!;
    private BenchmarkTransactionHolder _transactionHolder = null!;
    private DocumentCollection<Person> _collection = null!;

    private ObjectId[] _ids = null!;
    private ObjectId _targetId;

    /// <summary>
    /// Tests setup.
    /// </summary>
    [GlobalSetup]
    public void Setup()
    {
        var temp = AppContext.BaseDirectory;
        var id = Guid.NewGuid().ToString("N");
        _docDbPath = Path.Combine(temp, $"bench_read_docdb_{id}.db");
        _docDbWalPath = Path.ChangeExtension(_docDbPath, ".wal");

        if (File.Exists(_docDbPath)) File.Delete(_docDbPath);
        if (File.Exists(_docDbWalPath)) File.Delete(_docDbWalPath);

        _storage = new StorageEngine(_docDbPath, PageFileConfig.Default);
        _transactionHolder = new BenchmarkTransactionHolder(_storage);
        _collection = new DocumentCollection<Person>(_storage, _transactionHolder, new PersonMapper());

        _ids = new ObjectId[DocCount];
        for (int i = 0; i < DocCount; i++)
        {
            var p = CreatePerson(i);
            _ids[i] = _collection.Insert(p);
        }
        _transactionHolder.CommitAndReset();

        _targetId = _ids[DocCount / 2];
    }

    /// <summary>
    /// Tests cleanup.
    /// </summary>
    [GlobalCleanup]
    public void Cleanup()
    {
        _transactionHolder?.Dispose();
        _storage?.Dispose();

        if (File.Exists(_docDbPath)) File.Delete(_docDbPath);
        if (File.Exists(_docDbWalPath)) File.Delete(_docDbWalPath);
    }

    private Person CreatePerson(int i)
    {
        var p = new Person
        {
            Id = ObjectId.NewObjectId(),
            FirstName = $"First_{i}",
            LastName = $"Last_{i}",
            Age = 20 + (i % 50),
            Bio = null,
            CreatedAt = DateTime.UtcNow,
            Balance = 1000.50m * (i + 1),
            HomeAddress = new Address
            {
                Street = $"{i} Main St",
                City = "Tech City",
                ZipCode = "12345"
            }
        };

        // Add 10 work history items
        for (int j = 0; j < 10; j++)
        {
            p.EmploymentHistory.Add(new WorkHistory
            {
                CompanyName = $"TechCorp_{i}_{j}",
                Title = "Developer",
                DurationYears = j,
                Tags = new List<string> { "C#", "BSON", "Performance", "Database", "Complex" }
            });
        }

        return p;
    }

    /// <summary>
    /// Tests document db find by id.
    /// </summary>
    [Benchmark(Baseline = true, Description = "CBDD FindById")]
    [BenchmarkCategory("Read_Single")]
    public Person? DocumentDb_FindById()
    {
        return _collection.FindById(_targetId);
    }
}
