using Microsoft.Data.SqlClient;
using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.SiteCalls;

/// <summary>
/// Direct-SQL seeding helper for the Site Calls page Playwright E2E tests
/// (Site Call Audit #22, follow-ups Task 6).
///
/// <para>
/// The Site Calls page reads the central <c>SiteCalls</c> table through the
/// <c>SiteCallAuditActor</c>, which is a pure read-from-table mirror — so a row
/// INSERTed directly into <c>SiteCalls</c> surfaces on the page exactly as a
/// telemetry-ingested row would. Mirrors <see cref="Audit.AuditDataSeeder"/>:
/// each test inserts its own rows at setup and best-effort deletes them at
/// teardown, keeping the suite self-contained without touching
/// <c>infra/mssql/seed-config.sql</c>.
/// </para>
///
/// <para>
/// Rows are tagged with a unique <c>Target</c> prefix derived from the test name
/// + a GUID so the teardown <c>DELETE</c> never touches rows the cluster itself
/// produced. <c>CreatedAtUtc</c>/<c>UpdatedAtUtc</c> are pinned to "now" so the
/// page's default (unconstrained) query window sees the row.
/// </para>
/// </summary>
internal static class SiteCallDataSeeder
{
    /// <summary>
    /// Connection string for the running cluster's configuration DB.
    /// Delegates to <see cref="PlaywrightDbConnection.ConnectionString"/>.
    /// </summary>
    public static string ConnectionString => PlaywrightDbConnection.ConnectionString;

    /// <summary>
    /// Inserts a single row into the central <c>SiteCalls</c> table. Optional
    /// fields are nullable so a test can shape the row to the status/channel it
    /// needs for its grid assertions. <c>TrackedOperationId</c> is stored as the
    /// 36-character GUID string the entity mapping expects.
    /// </summary>
    public static async Task InsertSiteCallAsync(
        Guid trackedOperationId,
        string channel,
        string target,
        string sourceSite,
        string status,
        int retryCount,
        DateTime createdAtUtc,
        DateTime updatedAtUtc,
        string? lastError = null,
        int? httpStatus = null,
        DateTime? terminalAtUtc = null,
        CancellationToken ct = default)
    {
        const string sql = @"
INSERT INTO [SiteCalls]
([TrackedOperationId], [Channel], [Target], [SourceSite], [Status], [RetryCount],
 [LastError], [HttpStatus], [CreatedAtUtc], [UpdatedAtUtc], [TerminalAtUtc], [IngestedAtUtc])
VALUES
(@id, @channel, @target, @sourceSite, @status, @retryCount,
 @lastError, @httpStatus, @createdAtUtc, @updatedAtUtc, @terminalAtUtc, SYSUTCDATETIME());";

        await using var connection = new SqlConnection(ConnectionString);
        await connection.OpenAsync(ct);
        await using var cmd = connection.CreateCommand();
        cmd.CommandText = sql;
        cmd.Parameters.AddWithValue("@id", trackedOperationId.ToString());
        cmd.Parameters.AddWithValue("@channel", channel);
        cmd.Parameters.AddWithValue("@target", target);
        cmd.Parameters.AddWithValue("@sourceSite", sourceSite);
        cmd.Parameters.AddWithValue("@status", status);
        cmd.Parameters.AddWithValue("@retryCount", retryCount);
        cmd.Parameters.AddWithValue("@lastError", (object?)lastError ?? DBNull.Value);
        cmd.Parameters.AddWithValue("@httpStatus", (object?)httpStatus ?? DBNull.Value);
        cmd.Parameters.AddWithValue("@createdAtUtc", createdAtUtc);
        cmd.Parameters.AddWithValue("@updatedAtUtc", updatedAtUtc);
        cmd.Parameters.AddWithValue("@terminalAtUtc", (object?)terminalAtUtc ?? DBNull.Value);

        await cmd.ExecuteNonQueryAsync(ct);
    }

    /// <summary>
    /// Best-effort cleanup. Deletes every <c>SiteCalls</c> row whose <c>Target</c>
    /// starts with <paramref name="targetPrefix"/>. Swallows all errors — the
    /// prefix carries a per-run GUID so the rows are unique to this test run.
    /// </summary>
    public static async Task DeleteByTargetPrefixAsync(string targetPrefix, CancellationToken ct = default)
    {
        try
        {
            await using var connection = new SqlConnection(ConnectionString);
            await connection.OpenAsync(ct);
            await using var cmd = connection.CreateCommand();
            cmd.CommandText = "DELETE FROM [SiteCalls] WHERE [Target] LIKE @prefix";
            cmd.Parameters.AddWithValue("@prefix", targetPrefix + "%");
            await cmd.ExecuteNonQueryAsync(ct);
        }
        catch
        {
            // Best-effort — the prefix carries a GUID so the rows are unique to
            // this test run and won't collide on the next pass.
        }
    }

    /// <summary>
    /// Probe whether the configuration DB is reachable. Tests gate their per-test
    /// setup on this so a downed cluster surfaces a clear message rather than an
    /// opaque <see cref="SqlException"/>.
    /// </summary>
    public static async Task<bool> IsAvailableAsync(CancellationToken ct = default)
    {
        try
        {
            await using var connection = new SqlConnection(ConnectionString);
            await connection.OpenAsync(ct);
            return true;
        }
        catch
        {
            return false;
        }
    }
}
