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 Microsoft.Extensions.Logging;
using Serilog.Context;
using System.IO;

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


[InProcess]
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[HtmlExporter]
[JsonExporterAttribute.Full]
public class InsertBenchmarks
{
    private const int BatchSize = 1000;
    private static readonly ILogger Logger = Logging.CreateLogger<InsertBenchmarks>();

    private string _docDbPath = "";
    private string _docDbWalPath = "";

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

    private Person[] _batchData = Array.Empty<Person>();
    private Person? _singlePerson = null;

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

        _singlePerson = CreatePerson(0);
        _batchData = new Person[BatchSize];
        for (int i = 0; i < BatchSize; i++)
        {
            _batchData[i] = CreatePerson(i);
        }
    }

    private Person CreatePerson(int i)
    {
        var p = new Person
        {
            Id = ObjectId.NewObjectId(),
            FirstName = $"First_{i}",
            LastName = $"Last_{i}",
            Age = 20 + (i % 50),
            Bio = null, // Removed large payload to focus on structure
            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 to stress structure traversal
        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 iteration setup.
    /// </summary>
    [IterationSetup]
    public void IterationSetup()
    {
        _storage = new StorageEngine(_docDbPath, PageFileConfig.Default);
        _transactionHolder = new BenchmarkTransactionHolder(_storage);
        _collection = new DocumentCollection<Person>(_storage, _transactionHolder, new PersonMapper());
    }

    /// <summary>
    /// Tests cleanup.
    /// </summary>
    [IterationCleanup]
    public void Cleanup()
    {
        try
        {
            using var _ = LogContext.PushProperty("Benchmark", nameof(InsertBenchmarks));
            _transactionHolder?.Dispose();
            _transactionHolder = null;
            _storage?.Dispose();
            _storage = null;

            System.Threading.Thread.Sleep(100);

            if (File.Exists(_docDbPath)) File.Delete(_docDbPath);
            if (File.Exists(_docDbWalPath)) File.Delete(_docDbWalPath);
        }
        catch (Exception ex)
        {
            Logger.LogWarning(ex, "Cleanup warning");
        }
    }

    // --- Benchmarks ---

    /// <summary>
    /// Tests document db insert single.
    /// </summary>
    [Benchmark(Baseline = true, Description = "CBDD Single Insert")]
    [BenchmarkCategory("Insert_Single")]
    public void DocumentDb_Insert_Single()
    {
        _collection?.Insert(_singlePerson!);
        _transactionHolder?.CommitAndReset();
    }

    /// <summary>
    /// Tests document db insert batch.
    /// </summary>
    [Benchmark(Description = "CBDD Batch Insert (1000 items, 1 Txn)")]
    [BenchmarkCategory("Insert_Batch")]
    public void DocumentDb_Insert_Batch()
    {
        _collection?.InsertBulk(_batchData);
        _transactionHolder?.CommitAndReset();
    }
}
