using Microsoft.Data.Sqlite;

namespace ZB.MOM.WW.ScadaBridge.SiteEventLogging;

/// <summary>
/// DDL for the <c>site_events</c> table, extracted from <see cref="SiteEventLogger"/> so
/// it can be applied by whoever owns the database file.
/// <para>
/// Under LocalDb Phase 1 that owner is the Host's <c>AddZbLocalDb</c> onReady callback:
/// the table must exist before <c>ILocalDb.RegisterReplicated</c> installs its capture
/// triggers, and rows written before registration are never captured or snapshotted.
/// </para>
/// <para>
/// <b>Schema change.</b> The primary key is a TEXT GUID minted by the writer, replacing
/// the historical <c>INTEGER PRIMARY KEY AUTOINCREMENT</c>. This is required, not
/// cosmetic: replication resolves conflicts by last-writer-wins on the primary key, so
/// two site nodes each minting autoincrement id 1 for unrelated events would be treated
/// as one row and silently overwrite each other. GUID keys make the event log a pure
/// union across the pair. Consumers that relied on the id being monotonic — the keyset
/// paging cursor and the storage-cap purge — order by <c>timestamp</c> instead.
/// </para>
/// <para>
/// Deliberately depends only on <c>Microsoft.Data.Sqlite</c>, not on the LocalDb
/// library — the Host needs to apply this DDL to a LocalDb-managed connection, but
/// nothing about the schema itself is LocalDb-specific.
/// </para>
/// </summary>
public static class SiteEventLogSchema
{
    /// <summary>
    /// Creates the <c>site_events</c> table and its indexes when absent. Idempotent —
    /// safe to run on every startup.
    /// </summary>
    /// <param name="connection">An open connection to the database that should hold the table.</param>
    public static void Apply(SqliteConnection connection)
    {
        ArgumentNullException.ThrowIfNull(connection);

        // auto_vacuum must be set before any table is created for it to take effect
        // on a fresh database. With INCREMENTAL mode, PRAGMA incremental_vacuum can
        // later reclaim free pages so the storage-cap purge can shrink the file.
        using (var pragmaCmd = connection.CreateCommand())
        {
            pragmaCmd.CommandText = "PRAGMA auto_vacuum = INCREMENTAL";
            pragmaCmd.ExecuteNonQuery();
        }

        using var cmd = connection.CreateCommand();
        cmd.CommandText = """
            CREATE TABLE IF NOT EXISTS site_events (
                id TEXT NOT NULL PRIMARY KEY,
                timestamp TEXT NOT NULL,
                event_type TEXT NOT NULL,
                severity TEXT NOT NULL,
                instance_id TEXT,
                source TEXT NOT NULL,
                message TEXT NOT NULL,
                details TEXT
            );
            CREATE INDEX IF NOT EXISTS idx_events_timestamp ON site_events(timestamp);
            CREATE INDEX IF NOT EXISTS idx_events_type ON site_events(event_type);
            CREATE INDEX IF NOT EXISTS idx_events_instance ON site_events(instance_id);
            CREATE INDEX IF NOT EXISTS idx_events_severity ON site_events(severity);
            """;
        // The query service also supports keyword search via leading-wildcard
        // LIKE on message/source. A leading-wildcard LIKE cannot use a B-tree
        // index, so that path intentionally full-scans; severity/event_type/
        // instance_id/timestamp filters above are all covered.
        cmd.ExecuteNonQuery();
    }
}
