using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Storage;
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Context;

var serilogLogger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .CreateLogger();

using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.ClearProviders();
    builder.AddSerilog(serilogLogger, dispose: true);
});

var logger = loggerFactory.CreateLogger("CBDD.Demo");

// Shared dictionaries for demo
var keyMap = new ConcurrentDictionary<string, ushort>(StringComparer.OrdinalIgnoreCase);
var keys = new ConcurrentDictionary<ushort, string>();

// Pre-register some keys
ushort nextId = 1;
foreach (var k in new[] { "_id", "name", "age", "active", "title", "timestamp", "score" })
{
    keyMap[k] = nextId;
    keys[nextId] = k;
    nextId++;
}

// Example 1: Creating and reading BSON documents
logger.LogInformation("=== DocumentDb Demo ===");

// 1. Create a BSON document using builder
var document = BsonDocument.Create(keyMap, builder =>
{
    builder
        .AddObjectId("_id", ObjectId.NewObjectId())
        .AddString("name", "John Doe")
        .AddInt32("age", 30)
        .AddBoolean("active", true);
});

logger.LogInformation("Document created: {DocumentSize} bytes", document.Size);

// 2. Read fields from document
if (document.TryGetString("name", out var name))
    logger.LogInformation("Name: {Name}", name);

if (document.TryGetInt32("age", out var age))
    logger.LogInformation("Age: {Age}", age);

if (document.TryGetObjectId("_id", out var id))
    logger.LogInformation("ID: {DocumentId}", id);

// 3. Manual BSON writing for zero-allocation scenarios
Span<byte> buffer = stackalloc byte[512];
var writer = new BsonSpanWriter(buffer, keyMap);

var sizePos = writer.BeginDocument();
writer.WriteString("title", "High Performance BSON");
writer.WriteInt64("timestamp", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
writer.WriteDouble("score", 98.5);
writer.EndDocument(sizePos);

logger.LogInformation("Manual BSON document: {DocumentSize} bytes", writer.Position);

// 4. Read it back
var reader = new BsonSpanReader(buffer[..writer.Position], keys);
var docSize = reader.ReadDocumentSize();
logger.LogInformation("Reading document of {DocumentSize} bytes...", docSize);

using var _ = LogContext.PushProperty("Phase", "ReadDocument");
while (reader.Remaining > 1)
{
    var type = reader.ReadBsonType();
    if (type == BsonType.EndOfDocument)
        break;

    var fieldName = reader.ReadElementHeader();
    
    switch (type)
    {
        case BsonType.String:
            logger.LogInformation("{Field}: {Value}", fieldName, reader.ReadString());
            break;
        case BsonType.Int64:
            logger.LogInformation("{Field}: {Value}", fieldName, reader.ReadInt64());
            break;
        case BsonType.Double:
            logger.LogInformation("{Field}: {Value}", fieldName, reader.ReadDouble());
            break;
    }
}

// 5. Storage engine demo - page-based file
var config = PageFileConfig.Default;
var pageFile = new PageFile("demo.db", config);

logger.LogInformation("Opening page file with {PageSize} byte pages...", config.PageSize);
pageFile.Open();

// Allocate a page
var pageId = pageFile.AllocatePage();
logger.LogInformation("Allocated page ID: {PageId}", pageId);

// Write a page header
Span<byte> pageBuffer = stackalloc byte[config.PageSize];
var header = new PageHeader
{
    PageId = pageId,
    PageType = PageType.Data,
    FreeBytes = (ushort)(config.PageSize - 32),
    NextPageId = 0,
    TransactionId = 1,
    Checksum = 0
};

header.WriteTo(pageBuffer);
pageFile.WritePage(pageId, pageBuffer);
logger.LogInformation("Wrote page header (Type: {PageType})", header.PageType);

// Read it back
Span<byte> readBuffer = stackalloc byte[config.PageSize];
pageFile.ReadPage(pageId, readBuffer);
var readHeader = PageHeader.ReadFrom(readBuffer);

logger.LogInformation(
    "Read page header: PageId={PageId}, Type={PageType}, Free={FreeBytes} bytes",
    readHeader.PageId,
    readHeader.PageType,
    readHeader.FreeBytes);

pageFile.Dispose();

logger.LogInformation("Demo completed successfully");
