using System.Collections.Concurrent;
using ZB.MOM.WW.CBDD.Core.Compression;
using ZB.MOM.WW.CBDD.Core.Transactions;

namespace ZB.MOM.WW.CBDD.Core.Storage;

/// <summary>
/// Central storage engine managing page-based storage with WAL for durability.
/// 
/// Architecture (WAL-based like SQLite/PostgreSQL):
/// - PageFile: Committed baseline (persistent on disk)
/// - WAL Cache: Uncommitted transaction writes (in-memory)
/// - Read: PageFile + WAL cache overlay (for Read Your Own Writes)
/// - Commit: Flush to WAL, clear cache
/// - Checkpoint: Merge WAL ? PageFile periodically
/// </summary>
public sealed partial class StorageEngine : IStorageEngine, IDisposable
{
    private readonly PageFile _pageFile;
    private readonly WriteAheadLog _wal;
    private readonly CompressionOptions _compressionOptions;
    private readonly CompressionService _compressionService;
    private readonly CompressionTelemetry _compressionTelemetry;
    private readonly StorageFormatMetadata _storageFormatMetadata;
    private readonly MaintenanceOptions _maintenanceOptions;
    private CDC.ChangeStreamDispatcher? _cdc;

    // WAL cache: TransactionId → (PageId → PageData)
    // Stores uncommitted writes for "Read Your Own Writes" isolation
    private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<uint, byte[]>> _walCache;

    // WAL index cache: PageId → PageData (from latest committed transaction)
    // Lazily populated on first read after commit
    private readonly ConcurrentDictionary<uint, byte[]> _walIndex;

    // Global lock for commit/checkpoint synchronization
    private readonly SemaphoreSlim _commitLock = new(1, 1);

    // Transaction Management
    private readonly ConcurrentDictionary<ulong, Transaction> _activeTransactions;
    private ulong _nextTransactionId;

    private const long MaxWalSize = 4 * 1024 * 1024; // 4MB

    /// <summary>
    /// Initializes a new instance of the <see cref="StorageEngine"/> class.
    /// </summary>
    /// <param name="databasePath">The database file path.</param>
    /// <param name="config">The page file configuration.</param>
    public StorageEngine(
        string databasePath,
        PageFileConfig config,
        CompressionOptions? compressionOptions = null,
        MaintenanceOptions? maintenanceOptions = null)
    {
        _compressionOptions = CompressionOptions.Normalize(compressionOptions);
        _compressionService = new CompressionService();
        _compressionTelemetry = new CompressionTelemetry();
        _maintenanceOptions = maintenanceOptions ?? new MaintenanceOptions();

        // Auto-derive WAL path
        var walPath = Path.ChangeExtension(databasePath, ".wal");

        // Initialize storage infrastructure
        _pageFile = new PageFile(databasePath, config);
        _pageFile.Open();

        _wal = new WriteAheadLog(walPath);
        _walCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<uint, byte[]>>();
        _walIndex = new ConcurrentDictionary<uint, byte[]>();
        _activeTransactions = new ConcurrentDictionary<ulong, Transaction>();
        _nextTransactionId = 1;
        _storageFormatMetadata = InitializeStorageFormatMetadata();

        // Recover from WAL if exists (crash recovery or resume after close)
        // This replays any committed transactions not yet checkpointed
        if (_wal.GetCurrentSize() > 0)
        {
            Recover();
        }

        _ = ResumeCompactionIfNeeded();

        InitializeDictionary();
        TryRunStartupMaintenance();

        // Create and start checkpoint manager
        // _checkpointManager = new Transactions.CheckpointManager(this);
        // _checkpointManager.StartAutoCheckpoint();
    }

    /// <summary>
    /// Page size for this storage engine
    /// </summary>
    public int PageSize => _pageFile.PageSize;

    /// <summary>
    /// Compression options for this engine instance.
    /// </summary>
    public CompressionOptions CompressionOptions => _compressionOptions;

    /// <summary>
    /// Compression codec service for payload roundtrip operations.
    /// </summary>
    public CompressionService CompressionService => _compressionService;

    /// <summary>
    /// Compression telemetry counters for this engine instance.
    /// </summary>
    public CompressionTelemetry CompressionTelemetry => _compressionTelemetry;

    /// <summary>
    /// Returns a point-in-time snapshot of compression telemetry counters.
    /// </summary>
    public CompressionStats GetCompressionStats() => _compressionTelemetry.GetSnapshot();

    internal StorageFormatMetadata StorageFormatMetadata => _storageFormatMetadata;

    /// <summary>
    /// Checks if a page is currently being modified by another active transaction.
    /// This is used to implement pessimistic locking for page allocation/selection.
    /// </summary>
    /// <param name="pageId">The page identifier to check.</param>
    /// <param name="excludingTxId">The transaction identifier to exclude from the check.</param>
    /// <returns><see langword="true"/> if another transaction holds the page; otherwise, <see langword="false"/>.</returns>
    public bool IsPageLocked(uint pageId, ulong excludingTxId)
    {
        foreach (var kvp in _walCache)
        {
            var txId = kvp.Key;
            if (txId == excludingTxId) continue;

            var txnPages = kvp.Value;
            if (txnPages.ContainsKey(pageId))
                return true;
        }
        return false;
    }

    /// <summary>
    /// Disposes the storage engine and closes WAL.
    /// </summary>
    public void Dispose()
    {
        // 1. Rollback any active transactions
        if (_activeTransactions != null)
        {
            foreach (var txn in _activeTransactions.Values)
            {
                try
                {
                    RollbackTransaction(txn.TransactionId);
                }
                catch { /* Ignore errors during dispose */ }
            }
            _activeTransactions.Clear();
        }

        // 2. Close WAL and PageFile
        _wal?.Dispose();
        _pageFile?.Dispose();
        _maintenanceGate?.Dispose();
        _commitLock?.Dispose();
    }

    /// <summary>
    /// Registers the change stream dispatcher used for CDC notifications.
    /// </summary>
    /// <param name="cdc">The change stream dispatcher instance.</param>
    internal void RegisterCdc(CDC.ChangeStreamDispatcher cdc)
    {
        _cdc = cdc;
    }

    /// <summary>
    /// Gets the registered change stream dispatcher, if available.
    /// </summary>
    internal CDC.ChangeStreamDispatcher? Cdc => _cdc;
    void IStorageEngine.RegisterCdc(CDC.ChangeStreamDispatcher cdc) => RegisterCdc(cdc);
    CDC.ChangeStreamDispatcher? IStorageEngine.Cdc => _cdc;
    CompressionOptions IStorageEngine.CompressionOptions => _compressionOptions;
    CompressionService IStorageEngine.CompressionService => _compressionService;
    CompressionTelemetry IStorageEngine.CompressionTelemetry => _compressionTelemetry;
}
