import type { JsonValue, ApiEvent, ApiNode, ApiNodeDockerStatus, ApiNodeSystemStatus, ApiTask, ProviderLabels } from "../../shared/src/index";
import { ctx, sql, config, logger } from "./context";
import type { RawTaskRow, ScheduledTaskRow, ScheduledTaskRunRow, SqlClient } from "./types";
import { compactJson, nestedNumber, redactDatabaseUrl, rowIso } from "./http";
import { compactJsonForStorage } from "./json-storage";

function errorToJson(error: unknown): JsonValue {
  if (error instanceof Error) {
    return { name: error.name, message: error.message, stack: error.stack ?? null };
  }
  return String(error);
}

export async function initDatabase(client: SqlClient): Promise<void> {
  logger("info", "database_init_start", { databaseUrl: redactDatabaseUrl(config().databaseUrl) });
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_nodes (
      provider_id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      labels JSONB NOT NULL DEFAULT '{}'::jsonb,
      status TEXT NOT NULL,
      connected_at TIMESTAMPTZ,
      last_heartbeat TIMESTAMPTZ,
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_events (
      id BIGSERIAL PRIMARY KEY,
      type TEXT NOT NULL,
      source TEXT NOT NULL,
      payload JSONB NOT NULL DEFAULT '{}'::jsonb,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_tasks (
      id TEXT PRIMARY KEY,
      provider_id TEXT NOT NULL,
      command TEXT NOT NULL,
      status TEXT NOT NULL,
      payload JSONB NOT NULL DEFAULT '{}'::jsonb,
      result JSONB,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_scheduled_tasks (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      description TEXT NOT NULL DEFAULT '',
      enabled BOOLEAN NOT NULL DEFAULT true,
      schedule_json JSONB NOT NULL DEFAULT '{}'::jsonb,
      action_json JSONB NOT NULL DEFAULT '{}'::jsonb,
      concurrency_policy TEXT NOT NULL DEFAULT 'skip',
      next_run_at TIMESTAMPTZ,
      last_run_at TIMESTAMPTZ,
      last_run_id TEXT,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_scheduled_task_runs (
      id TEXT PRIMARY KEY,
      schedule_id TEXT NOT NULL REFERENCES unidesk_scheduled_tasks(id) ON DELETE CASCADE,
      trigger_type TEXT NOT NULL,
      status TEXT NOT NULL,
      task_id TEXT,
      result JSONB,
      error TEXT,
      started_at TIMESTAMPTZ,
      finished_at TIMESTAMPTZ,
      duration_ms BIGINT,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_node_docker_status (
      provider_id TEXT PRIMARY KEY,
      status JSONB NOT NULL DEFAULT '{}'::jsonb,
      collected_at TIMESTAMPTZ,
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_node_system_status (
      provider_id TEXT PRIMARY KEY,
      status JSONB NOT NULL DEFAULT '{}'::jsonb,
      collected_at TIMESTAMPTZ,
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`
    CREATE TABLE IF NOT EXISTS unidesk_node_metric_samples (
      id BIGSERIAL PRIMARY KEY,
      provider_id TEXT NOT NULL,
      collected_at TIMESTAMPTZ NOT NULL,
      cpu_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
      memory_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
      disk_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
      sample JSONB NOT NULL DEFAULT '{}'::jsonb,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )
  `;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_updated_at ON unidesk_tasks(updated_at DESC)`;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_status_updated_at ON unidesk_tasks(status, updated_at DESC)`;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_tasks_next_run ON unidesk_scheduled_tasks(enabled, next_run_at)`;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_task_runs_schedule_updated ON unidesk_scheduled_task_runs(schedule_id, updated_at DESC)`;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_task_runs_status_updated ON unidesk_scheduled_task_runs(status, updated_at DESC)`;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_node_system_status_updated_at ON unidesk_node_system_status(updated_at DESC)`;
  await client`CREATE INDEX IF NOT EXISTS idx_unidesk_node_metric_samples_provider_time ON unidesk_node_metric_samples(provider_id, collected_at DESC)`;
  ctx.dbReady = true;
  logger("info", "database_init_complete");
}

export async function recordEvent(type: string, source: string, payload: Record<string, JsonValue>): Promise<void> {
  logger("info", type, { source, payload });
  if (!ctx.dbReady) return;
  const storedPayload = compactJsonForStorage(payload);
  try {
    await sql()`
      INSERT INTO unidesk_events (type, source, payload)
      VALUES (${type}, ${source}, ${sql().json(storedPayload)})
    `;
  } catch (error) {
    logger("error", "event_insert_failed", { type, source, error: errorToJson(error) });
  }
}

export async function upsertProviderNode(providerId: string, name: string, labels: unknown): Promise<void> {
  await sql()`
    INSERT INTO unidesk_nodes (provider_id, name, labels, status, connected_at, last_heartbeat, updated_at)
    VALUES (${providerId}, ${name}, ${sql().json(labels as JsonValue)}, 'online', now(), now(), now())
    ON CONFLICT (provider_id) DO UPDATE SET
      name = EXCLUDED.name,
      labels = EXCLUDED.labels,
      status = 'online',
      connected_at = COALESCE(unidesk_nodes.connected_at, EXCLUDED.connected_at),
      last_heartbeat = now(),
      updated_at = now()
  `;
}

export async function updateProviderHeartbeat(providerId: string, labels: unknown): Promise<void> {
  await sql()`
    UPDATE unidesk_nodes
    SET labels = unidesk_nodes.labels || ${sql().json(labels as JsonValue)}, status = 'online', last_heartbeat = now(), updated_at = now()
    WHERE provider_id = ${providerId}
  `;
}
export async function upsertDockerStatus(providerId: string, status: unknown, collectedAt: string): Promise<void> {
  await sql()`
    INSERT INTO unidesk_node_docker_status (provider_id, status, collected_at, updated_at)
    VALUES (${providerId}, ${sql().json(status as JsonValue)}, ${collectedAt}, now())
    ON CONFLICT (provider_id) DO UPDATE SET
      status = EXCLUDED.status,
      collected_at = EXCLUDED.collected_at,
      updated_at = now()
  `;
}

export async function upsertSystemStatus(providerId: string, status: unknown, collectedAt: string): Promise<void> {
  const cpuPercent = nestedNumber(status, "cpu", "percent");
  const memoryPercent = nestedNumber(status, "memory", "percent");
  const diskPercent = nestedNumber(status, "disk", "percent");
  await sql().begin(async (tx) => {
    await tx`
      INSERT INTO unidesk_node_system_status (provider_id, status, collected_at, updated_at)
      VALUES (${providerId}, ${tx.json(status as JsonValue)}, ${collectedAt}, now())
      ON CONFLICT (provider_id) DO UPDATE SET
        status = EXCLUDED.status,
        collected_at = EXCLUDED.collected_at,
        updated_at = now()
    `;
    await tx`
      INSERT INTO unidesk_node_metric_samples (provider_id, collected_at, cpu_percent, memory_percent, disk_percent, sample)
      VALUES (${providerId}, ${collectedAt}, ${cpuPercent}, ${memoryPercent}, ${diskPercent}, ${tx.json(status as JsonValue)})
    `;
    await tx`
      DELETE FROM unidesk_node_metric_samples
      WHERE provider_id = ${providerId}
        AND id NOT IN (
          SELECT id FROM unidesk_node_metric_samples
          WHERE provider_id = ${providerId}
          ORDER BY collected_at DESC
          LIMIT 720
        )
    `;
  });
}

export async function getNodes(): Promise<ApiNode[]> {
  const rows = await sql()<Array<Record<string, unknown>>>`
    SELECT provider_id, name, labels, status, connected_at, last_heartbeat
    FROM unidesk_nodes
    ORDER BY status DESC, provider_id ASC
  `;
  return rows.map((row) => ({
    providerId: String(row.provider_id),
    name: String(row.name),
    status: row.status === "online" ? "online" : "offline",
    labels: (row.labels ?? {}) as ProviderLabels,
    connectedAt: row.connected_at instanceof Date ? row.connected_at.toISOString() : row.connected_at === null ? null : String(row.connected_at),
    lastHeartbeat: row.last_heartbeat instanceof Date ? row.last_heartbeat.toISOString() : row.last_heartbeat === null ? null : String(row.last_heartbeat),
  }));
}
export async function getNodeDockerStatuses(): Promise<ApiNodeDockerStatus[]> {
  const rows = await sql()<Array<Record<string, unknown>>>`
    SELECT n.provider_id, n.name, n.status AS node_status, d.status AS docker_status, d.updated_at
    FROM unidesk_nodes n
    LEFT JOIN unidesk_node_docker_status d ON d.provider_id = n.provider_id
    ORDER BY n.status DESC, n.provider_id ASC
  `;
  return rows.map((row) => ({
    providerId: String(row.provider_id),
    name: String(row.name),
    nodeStatus: row.node_status === "online" ? "online" : "offline",
    dockerStatus: row.docker_status === null || row.docker_status === undefined ? null : (row.docker_status as JsonValue),
    updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : row.updated_at === null || row.updated_at === undefined ? null : String(row.updated_at),
  }));
}

function metricPointFromSample(sample: unknown, collectedAt: string): JsonValue {
  return {
    at: collectedAt,
    cpuPercent: nestedNumber(sample, "cpu", "percent"),
    memoryPercent: nestedNumber(sample, "memory", "percent"),
    diskPercent: nestedNumber(sample, "disk", "percent"),
    memoryUsedBytes: nestedNumber(sample, "memory", "usedBytes"),
    memoryTotalBytes: nestedNumber(sample, "memory", "totalBytes"),
    diskUsedBytes: nestedNumber(sample, "disk", "usedBytes"),
    diskTotalBytes: nestedNumber(sample, "disk", "totalBytes"),
    load1: nestedNumber(sample, "cpu", "load1"),
  };
}

const SYSTEM_STATUS_STALE_AFTER_SECONDS = 300;

function systemStatusAgeSeconds(collectedAt: unknown, now: Date): number | null {
  const date = collectedAt instanceof Date ? collectedAt : typeof collectedAt === "string" ? new Date(collectedAt) : null;
  if (date === null || Number.isNaN(date.getTime())) return null;
  return Math.max(0, Math.floor((now.getTime() - date.getTime()) / 1000));
}

function systemStatusIsStale(collectedAt: unknown, now: Date): boolean {
  const ageSeconds = systemStatusAgeSeconds(collectedAt, now);
  return ageSeconds !== null && ageSeconds > SYSTEM_STATUS_STALE_AFTER_SECONDS;
}

export async function getNodeSystemStatuses(limit: number): Promise<ApiNodeSystemStatus[]> {
  const currentRows = await sql()<Array<Record<string, unknown>>>`
    SELECT n.provider_id, n.name, n.status AS node_status, s.status AS system_status, s.collected_at AS system_collected_at, s.updated_at
    FROM unidesk_nodes n
    LEFT JOIN unidesk_node_system_status s ON s.provider_id = n.provider_id
    ORDER BY n.status DESC, n.provider_id ASC
  `;
  const sampleRows = await sql()<Array<Record<string, unknown>>>`
    SELECT n.provider_id, recent.collected_at, recent.sample
    FROM unidesk_nodes n
    LEFT JOIN LATERAL (
      SELECT collected_at, sample
      FROM unidesk_node_metric_samples m
      WHERE m.provider_id = n.provider_id
      ORDER BY collected_at DESC
      LIMIT ${limit}
    ) recent ON true
    WHERE recent.collected_at IS NOT NULL
    ORDER BY n.provider_id ASC, recent.collected_at ASC
  `;
  const historyByProvider = new Map<string, JsonValue[]>();
  for (const row of sampleRows) {
    const providerId = String(row.provider_id);
    const collectedAt = row.collected_at instanceof Date ? row.collected_at.toISOString() : String(row.collected_at);
    const history = historyByProvider.get(providerId) ?? [];
    history.push(metricPointFromSample(row.sample ?? {}, collectedAt));
    historyByProvider.set(providerId, history);
  }
  const now = new Date();
  return currentRows.map((row) => {
    const providerId = String(row.provider_id);
    const lastKnown = row.system_status === null || row.system_status === undefined ? null : (row.system_status as JsonValue);
    const currentCollectedAt = row.system_collected_at instanceof Date ? row.system_collected_at.toISOString() : row.system_collected_at === null || row.system_collected_at === undefined ? null : String(row.system_collected_at);
    const stale = systemStatusIsStale(row.system_collected_at, now);
    return {
      providerId,
      name: String(row.name),
      nodeStatus: row.node_status === "online" ? "online" : "offline",
      current: stale ? null : lastKnown,
      lastKnown,
      currentCollectedAt,
      stale,
      staleSeconds: systemStatusAgeSeconds(row.system_collected_at, now),
      history: historyByProvider.get(providerId) ?? [],
      updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : row.updated_at === null || row.updated_at === undefined ? null : String(row.updated_at),
    };
  });
}

export async function getEvents(limit: number): Promise<ApiEvent[]> {
  const rows = await sql()<Array<Record<string, unknown>>>`
    SELECT id, type, source, payload, created_at
    FROM unidesk_events
    ORDER BY id DESC
    LIMIT ${limit}
  `;
  return rows.map((row) => ({
    id: Number(row.id),
    type: String(row.type),
    source: String(row.source),
    payload: compactJson(row.payload ?? {}),
    createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),
  }));
}

function rowString(row: Record<string, unknown>, key: string): string {
  const value = row[key];
  return typeof value === "string" ? value : value === null || value === undefined ? "" : String(value);
}

function rowNumber(row: Record<string, unknown>, key: string): number | null {
  const value = row[key];
  const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
  return Number.isFinite(parsed) ? parsed : null;
}

function taskJsonSummary(row: Record<string, unknown>, prefix: "payload" | "result"): JsonValue {
  const type = rowString(row, `${prefix}_type`);
  if (type.length === 0) return prefix === "result" ? null : {};
  const summary: Record<string, JsonValue> = { summaryOnly: true, type };
  const fields = prefix === "payload"
    ? ["source", "serviceId", "method", "path", "mode", "targetBaseUrl", "timeoutMs", "targetProviderGatewayVersion", "providerGatewayVersion"]
    : ["error", "reason", "message", "status", "exitCode", "code", "signal", "timeoutMs", "previousStatus", "mode", "policy", "targetProviderGatewayVersion", "providerGatewayVersion", "updaterContainerId"];
  for (const field of fields) {
    const value = row[`${prefix}_${field}`];
    if (value !== null && value !== undefined && String(value).length > 0) {
      summary[field] = typeof value === "number" || typeof value === "boolean" ? value : String(value);
    }
  }
  const bodyChars = rowNumber(row, `${prefix}_body_text_chars`);
  if (bodyChars !== null) summary.bodyText = `<omitted:${bodyChars} chars>`;
  return summary;
}
function taskSummaryFromRow(row: Record<string, unknown>): ApiTask {
  return {
    id: String(row.id),
    providerId: String(row.provider_id),
    command: String(row.command),
    status: String(row.status),
    payload: taskJsonSummary(row, "payload"),
    result: taskJsonSummary(row, "result"),
    createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),
    updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),
    _summaryOnly: true,
  } as ApiTask;
}

export async function getTask(taskId: string): Promise<ApiTask | null> {
  const rows = await sql()<Array<Record<string, unknown>>>`
    SELECT
      id,
      provider_id,
      command,
      status,
      CASE
        WHEN payload ? 'bodyText' THEN jsonb_set(payload - 'bodyText', '{bodyText}', to_jsonb(('<omitted:' || length(payload->>'bodyText')::text || ' chars>')::text))
        ELSE payload
      END AS payload,
      CASE
        WHEN result IS NOT NULL AND result ? 'bodyText' THEN jsonb_set(result - 'bodyText', '{bodyText}', to_jsonb(('<omitted:' || length(result->>'bodyText')::text || ' chars>')::text))
        ELSE result
      END AS result,
      created_at,
      updated_at
    FROM unidesk_tasks
    WHERE id = ${taskId}
    LIMIT 1
  `;
  const row = rows[0];
  if (row === undefined) return null;
  return {
    id: String(row.id),
    providerId: String(row.provider_id),
    command: String(row.command),
    status: String(row.status),
    payload: compactJson(row.payload ?? {}),
    result: compactJson(row.result ?? null),
    createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),
    updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),
  };
}
let lastTaskSweepAt = 0;
let taskSweepInFlight: Promise<void> | null = null;

async function markStaleTasksFailed(): Promise<void> {
  if (!ctx.dbReady) return;
  const timeoutMs = config().taskPendingTimeoutMs;
  const rows = await sql()<Array<{ id: string; provider_id: string; command: string; previous_status: string; updated_at: Date | string }>>`
    WITH stale AS (
      SELECT id, provider_id, command, status AS previous_status, updated_at
      FROM unidesk_tasks
      WHERE status IN ('queued', 'dispatched', 'running')
        AND updated_at < now() - (${timeoutMs}::bigint * interval '1 millisecond')
      FOR UPDATE
    ),
    updated AS (
      UPDATE unidesk_tasks task
      SET
        status = 'failed',
        result = jsonb_build_object(
          'error', 'task timed out without terminal provider status',
          'timeoutMs', ${timeoutMs}::bigint,
          'previousStatus', stale.previous_status,
          'previousResult', task.result,
          'timedOutAt', now()
        ),
        updated_at = now()
      FROM stale
      WHERE task.id = stale.id
      RETURNING task.id, task.provider_id, task.command, stale.previous_status, stale.updated_at
    )
    SELECT * FROM updated
  `;
  for (const row of rows) {
    await recordEvent("task_timeout", row.provider_id, {
      taskId: row.id,
      providerId: row.provider_id,
      command: row.command,
      previousStatus: row.previous_status,
      previousUpdatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),
      timeoutMs,
    });
  }
}

async function maybeMarkStaleTasksFailed(minIntervalMs = 15_000): Promise<void> {
  if (!ctx.dbReady) return;
  const now = Date.now();
  if (taskSweepInFlight !== null) return taskSweepInFlight;
  if (now - lastTaskSweepAt < minIntervalMs) return;
  lastTaskSweepAt = now;
  taskSweepInFlight = markStaleTasksFailed()
    .catch((error) => {
      logger("error", "task_timeout_sweep_failed", { error: errorToJson(error) });
    })
    .finally(() => {
      taskSweepInFlight = null;
    });
  return taskSweepInFlight;
}
export async function getTasks(limit: number, statusFilter = "all", lite = false, summary = false): Promise<ApiTask[]> {
  await maybeMarkStaleTasksFailed();
  if (summary && !lite) {
    const rows = statusFilter === "pending"
      ? await sql()<Array<Record<string, unknown>>>`
        SELECT
          id,
          provider_id,
          command,
          status,
          jsonb_typeof(payload) AS payload_type,
          payload->>'source' AS "payload_source",
          payload->>'serviceId' AS "payload_serviceId",
          payload->>'method' AS "payload_method",
          payload->>'path' AS "payload_path",
          payload->>'mode' AS "payload_mode",
          payload->>'targetBaseUrl' AS "payload_targetBaseUrl",
          payload->>'timeoutMs' AS "payload_timeoutMs",
          payload->>'targetProviderGatewayVersion' AS "payload_targetProviderGatewayVersion",
          payload->>'providerGatewayVersion' AS "payload_providerGatewayVersion",
          CASE WHEN payload ? 'bodyText' THEN length(payload->>'bodyText') ELSE NULL END AS payload_body_text_chars,
          jsonb_typeof(result) AS result_type,
          result->>'error' AS "result_error",
          result->>'reason' AS "result_reason",
          result->>'message' AS "result_message",
          result->>'status' AS "result_status",
          result->>'exitCode' AS "result_exitCode",
          result->>'code' AS "result_code",
          result->>'signal' AS "result_signal",
          result->>'timeoutMs' AS "result_timeoutMs",
          result->>'previousStatus' AS "result_previousStatus",
          result->>'mode' AS "result_mode",
          result->>'policy' AS "result_policy",
          result->>'targetProviderGatewayVersion' AS "result_targetProviderGatewayVersion",
          result->>'providerGatewayVersion' AS "result_providerGatewayVersion",
          result->>'updaterContainerId' AS "result_updaterContainerId",
          CASE WHEN result IS NOT NULL AND result ? 'bodyText' THEN length(result->>'bodyText') ELSE NULL END AS result_body_text_chars,
          created_at,
          updated_at
        FROM unidesk_tasks
        WHERE status IN ('queued', 'dispatched', 'running')
        ORDER BY updated_at DESC
        LIMIT ${limit}
      `
      : await sql()<Array<Record<string, unknown>>>`
        SELECT
          id,
          provider_id,
          command,
          status,
          jsonb_typeof(payload) AS payload_type,
          payload->>'source' AS "payload_source",
          payload->>'serviceId' AS "payload_serviceId",
          payload->>'method' AS "payload_method",
          payload->>'path' AS "payload_path",
          payload->>'mode' AS "payload_mode",
          payload->>'targetBaseUrl' AS "payload_targetBaseUrl",
          payload->>'timeoutMs' AS "payload_timeoutMs",
          payload->>'targetProviderGatewayVersion' AS "payload_targetProviderGatewayVersion",
          payload->>'providerGatewayVersion' AS "payload_providerGatewayVersion",
          CASE WHEN payload ? 'bodyText' THEN length(payload->>'bodyText') ELSE NULL END AS payload_body_text_chars,
          jsonb_typeof(result) AS result_type,
          result->>'error' AS "result_error",
          result->>'reason' AS "result_reason",
          result->>'message' AS "result_message",
          result->>'status' AS "result_status",
          result->>'exitCode' AS "result_exitCode",
          result->>'code' AS "result_code",
          result->>'signal' AS "result_signal",
          result->>'timeoutMs' AS "result_timeoutMs",
          result->>'previousStatus' AS "result_previousStatus",
          result->>'mode' AS "result_mode",
          COALESCE(result->>'policy', result #>> '{plan,policy}') AS "result_policy",
          COALESCE(result->>'targetProviderGatewayVersion', result #>> '{plan,targetProviderGatewayVersion}') AS "result_targetProviderGatewayVersion",
          COALESCE(result->>'providerGatewayVersion', result #>> '{plan,providerGatewayVersion}') AS "result_providerGatewayVersion",
          result->>'updaterContainerId' AS "result_updaterContainerId",
          CASE WHEN result ? 'bodyText' THEN length(result->>'bodyText') ELSE NULL END AS result_body_text_chars,
          created_at,
          updated_at
        FROM unidesk_tasks
        ORDER BY updated_at DESC
        LIMIT ${limit}
      `;
    return rows.map(taskSummaryFromRow);
  }
  const rows = statusFilter === "pending"
    ? lite
      ? await sql()<Array<Record<string, unknown>>>`
        SELECT id, provider_id, command, status, created_at, updated_at
        FROM unidesk_tasks
        WHERE status IN ('queued', 'dispatched', 'running')
        ORDER BY updated_at DESC
        LIMIT ${limit}
      `
      : await sql()<Array<Record<string, unknown>>>`
        SELECT
          id,
          provider_id,
          command,
          status,
          CASE
            WHEN payload ? 'bodyText' THEN jsonb_set(payload - 'bodyText', '{bodyText}', to_jsonb(('<omitted:' || length(payload->>'bodyText')::text || ' chars>')::text))
            ELSE payload
          END AS payload,
          CASE
            WHEN result IS NOT NULL AND result ? 'bodyText' THEN jsonb_set(result - 'bodyText', '{bodyText}', to_jsonb(('<omitted:' || length(result->>'bodyText')::text || ' chars>')::text))
            ELSE result
          END AS result,
          created_at,
          updated_at
        FROM unidesk_tasks
        WHERE status IN ('queued', 'dispatched', 'running')
        ORDER BY updated_at DESC
        LIMIT ${limit}
      `
    : lite
      ? await sql()<Array<Record<string, unknown>>>`
        SELECT id, provider_id, command, status, created_at, updated_at
        FROM unidesk_tasks
        ORDER BY updated_at DESC
        LIMIT ${limit}
      `
      : await sql()<Array<Record<string, unknown>>>`
        SELECT
          id,
          provider_id,
          command,
          status,
          CASE
            WHEN payload ? 'bodyText' THEN jsonb_set(payload - 'bodyText', '{bodyText}', to_jsonb(('<omitted:' || length(payload->>'bodyText')::text || ' chars>')::text))
            ELSE payload
          END AS payload,
          CASE
            WHEN result IS NOT NULL AND result ? 'bodyText' THEN jsonb_set(result - 'bodyText', '{bodyText}', to_jsonb(('<omitted:' || length(result->>'bodyText')::text || ' chars>')::text))
            ELSE result
          END AS result,
          created_at,
          updated_at
        FROM unidesk_tasks
        ORDER BY updated_at DESC
        LIMIT ${limit}
      `;
  return rows.map((row) => ({
    id: String(row.id),
    providerId: String(row.provider_id),
    command: String(row.command),
    status: String(row.status),
    payload: lite ? {} : compactJson(row.payload ?? {}),
    result: lite ? null : compactJson(row.result ?? null),
    createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),
    updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),
  }));
}
