import type { JsonValue } from "../../shared/src/index";
import { ctx, sql, config, logger } from "./context";
import type { RawTaskRow, MicroserviceConfig } from "./types";
import { jsonResponse, compactJson, errorToJson } from "./http";
import { createAndSendTask, waitForTaskTerminal } from "./task-dispatcher";
import { microserviceById, getMicroservices, microserviceAvailability } from "./microservice-proxy";
import { getNodes, getNodeDockerStatuses } from "./db";

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

function shellQuote(value: string): string {
  return `'${value.replace(/'/g, `'\\''`)}'`;
}

function safePerfRunId(): string {
  return `codex_queue_perf_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;
}

function numberFromUnknown(value: unknown, fallback: number, min: number, max: number): number {
  const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : fallback;
  if (!Number.isFinite(parsed)) return fallback;
  return Math.max(min, Math.min(max, Math.floor(parsed)));
}

function recordFromJson(value: JsonValue | null): Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function parsePerfPoll(stdout: string): { ready: boolean; exitCode: number | null; body: string; stderr: string } {
  if (!stdout.includes("__UNIDESK_CODEX_QUEUE_PERF_READY__")) {
    return { ready: false, exitCode: null, body: "", stderr: "" };
  }
  const exitMatch = stdout.match(/__UNIDESK_CODEX_QUEUE_PERF_READY__\r?\n([0-9]+)/u);
  const bodyMatch = stdout.match(/__UNIDESK_CODEX_QUEUE_PERF_STDOUT__\r?\n([\s\S]*?)\r?\n__UNIDESK_CODEX_QUEUE_PERF_STDERR__/u);
  const stderrMatch = stdout.match(/__UNIDESK_CODEX_QUEUE_PERF_STDERR__\r?\n([\s\S]*)$/u);
  const exitCode = exitMatch === null ? null : Number(exitMatch[1]);
  return {
    ready: true,
    exitCode: Number.isFinite(exitCode) ? exitCode : null,
    body: bodyMatch?.[1]?.trim() ?? "",
    stderr: stderrMatch?.[1]?.trim() ?? "",
  };
}

function parsePerfJson(body: string): Record<string, unknown> | null {
  for (const line of body.split(/\r?\n/u).reverse()) {
    const trimmed = line.trim();
    if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) continue;
    try {
      const parsed = JSON.parse(trimmed) as unknown;
      if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed as Record<string, unknown>;
    } catch {
      // Continue scanning for the compact JSON line emitted by the Playwright script.
    }
  }
  return null;
}
// ---------------------------------------------------------------------------
// Exported helpers
// ---------------------------------------------------------------------------

export function rawTaskJson(task: RawTaskRow | null): JsonValue {
  if (task === null) return null;
  return {
    id: task.id,
    providerId: task.provider_id,
    command: task.command,
    status: task.status,
    payload: task.payload,
    result: task.result,
    updatedAt: task.updated_at instanceof Date ? task.updated_at.toISOString() : String(task.updated_at),
  };
}

export async function providerSupports(providerId: string, capability: string): Promise<boolean> {
  if (!ctx.dbReady) return false;
  const rows = await sql()<Array<{ labels: unknown }>>`
    SELECT labels
    FROM unidesk_nodes
    WHERE provider_id = ${providerId}
    LIMIT 1
  `;
  const labels = rows[0]?.labels;
  if (typeof labels !== "object" || labels === null || Array.isArray(labels)) return false;
  const capabilities = (labels as Record<string, unknown>).unideskCapabilities;
  return Array.isArray(capabilities) ? capabilities.filter((item): item is string => typeof item === "string").includes(capability) : false;
}

export async function runHostSshPerfCommand(providerId: string, command: string, timeoutMs = 18_000): Promise<{ ok: boolean; taskId: string; task: RawTaskRow | null; stdout: string; stderr: string; exitCode: number | null; timedOut: boolean }> {
  const { taskId, providerOnline } = await createAndSendTask(providerId, "host.ssh", {
    source: "code-queue-performance-panel",
    mode: "exec",
    cwd: "/root/unidesk",
    timeoutMs: 15_000,
    command,
  });
  if (!providerOnline) {
    return { ok: false, taskId, task: null, stdout: "", stderr: `provider is offline: ${providerId}`, exitCode: null, timedOut: false };
  }
  const task = await waitForTaskTerminal(taskId, timeoutMs);
  const result = recordFromJson(task?.result ?? null);
  return {
    ok: task?.status === "succeeded" && result.ok === true,
    taskId,
    task,
    stdout: typeof result.stdout === "string" ? result.stdout : "",
    stderr: typeof result.stderr === "string" ? result.stderr : "",
    exitCode: typeof result.exitCode === "number" ? result.exitCode : null,
    timedOut: result.timedOut === true,
  };
}
// ---------------------------------------------------------------------------
// countPendingTasks / getPgdataUsage / getMicroserviceAvailabilitySummary
// ---------------------------------------------------------------------------

async function countPendingTasks(): Promise<number> {
  const rows = await sql()<Array<{ count: string | number }>>`
    SELECT count(*)::int AS count
    FROM unidesk_tasks
    WHERE status IN ('queued', 'dispatched', 'running')
  `;
  return Number(rows[0]?.count ?? 0);
}

async function getPgdataUsage(): Promise<JsonValue> {
  const rows = await sql()<Array<{ database_name: string; database_bytes: string | number; database_pretty: string }>>`
    SELECT
      current_database()::text AS database_name,
      pg_database_size(current_database())::bigint AS database_bytes,
      pg_size_pretty(pg_database_size(current_database()))::text AS database_pretty
  `;
  const row = rows[0];
  return {
    volumeName: config().databaseVolumeName,
    volumeSize: config().databaseVolumeSize,
    databaseName: row?.database_name ?? "unknown",
    databaseBytes: Number(row?.database_bytes ?? 0),
    databasePretty: row?.database_pretty ?? "--",
  };
}

function isPlainRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

async function getMicroserviceAvailabilitySummary(): Promise<Record<string, JsonValue>> {
  const probes = await Promise.all(config().microservices.map((service) => microserviceAvailability(service)));
  const healthy = probes.filter((probe) => probe.healthy === true);
  const byProvider: Record<string, JsonValue> = {};
  for (const probe of probes) {
    const providerId = String(probe.providerId ?? "unknown");
    const current = isPlainRecord(byProvider[providerId]) ? byProvider[providerId] as Record<string, JsonValue> : { total: 0, healthy: 0, unhealthy: 0 };
    current.total = Number(current.total ?? 0) + 1;
    if (probe.healthy === true) current.healthy = Number(current.healthy ?? 0) + 1;
    else current.unhealthy = Number(current.unhealthy ?? 0) + 1;
    byProvider[providerId] = current;
  }
  return {
    totalCount: probes.length,
    healthyCount: healthy.length,
    unhealthyCount: probes.length - healthy.length,
    checkedAt: new Date().toISOString(),
    byProvider,
    services: probes.map((probe) => ({
      serviceId: probe.serviceId,
      name: probe.name,
      providerId: probe.providerId,
      healthy: probe.healthy,
      status: probe.status,
      reason: probe.reason,
      checkedAt: probe.checkedAt,
    })),
  };
}
// ---------------------------------------------------------------------------
// getOverview
// ---------------------------------------------------------------------------

export async function getOverview(): Promise<JsonValue> {
  const [nodeRows, dockerRows, systemRows, pendingTasks, pgdata, microserviceAvailabilitySummary] = await Promise.all([
    sql()<Array<{ node_count: string | number; online_node_count: string | number }>>`
      SELECT
        count(*)::int AS node_count,
        count(*) FILTER (WHERE status = 'online')::int AS online_node_count
      FROM unidesk_nodes
    `,
    sql()<Array<{ docker_status_node_count: string | number }>>`
      SELECT count(*) FILTER (WHERE d.status IS NOT NULL)::int AS docker_status_node_count
      FROM unidesk_nodes n
      LEFT JOIN unidesk_node_docker_status d ON d.provider_id = n.provider_id
    `,
    sql()<Array<{ system_status_node_count: string | number }>>`
      SELECT count(*) FILTER (WHERE s.status IS NOT NULL)::int AS system_status_node_count
      FROM unidesk_nodes n
      LEFT JOIN unidesk_node_system_status s ON s.provider_id = n.provider_id
    `,
    countPendingTasks(),
    getPgdataUsage(),
    getMicroserviceAvailabilitySummary(),
  ]);
  const nodeRow = nodeRows[0];
  const dockerRow = dockerRows[0];
  const systemRow = systemRows[0];
  return {
    service: "unidesk-core",
    ok: true,
    dbReady: ctx.dbReady,
    pgdata,
    uptimeSeconds: Math.floor((Date.now() - ctx.serviceStartedAt.getTime()) / 1000),
    nodeCount: Number(nodeRow?.node_count ?? 0),
    onlineNodeCount: Number(nodeRow?.online_node_count ?? 0),
    dockerStatusNodeCount: Number(dockerRow?.docker_status_node_count ?? 0),
    systemStatusNodeCount: Number(systemRow?.system_status_node_count ?? 0),
    pendingTaskCount: pendingTasks,
    microserviceAvailability: microserviceAvailabilitySummary,
    taskPendingTimeoutMs: config().taskPendingTimeoutMs,
    activeSocketCount: ctx.activeProviders.size,
    heartbeatTimeoutMs: config().heartbeatTimeoutMs,
  };
}
// ---------------------------------------------------------------------------
// codexQueueLoadTest
// ---------------------------------------------------------------------------

export async function codexQueueLoadTest(req: Request): Promise<Response> {
  const body = req.method === "POST" ? (await req.json().catch(() => ({}))) as Record<string, unknown> : {};
  const codexService = microserviceById("code-queue");
  const providerId = typeof body.providerId === "string" && body.providerId.length > 0
    ? body.providerId
    : codexService?.providerId ?? "main-server";
  if (!(await providerSupports(providerId, "host.ssh"))) {
    return jsonResponse({ ok: true, measurementOk: false, error: `provider does not declare host.ssh capability: ${providerId}`, providerId }, 200);
  }

  const timeoutMs = numberFromUnknown(body.timeoutMs, 90_000, 5_000, 180_000);
  const targetMs = numberFromUnknown(body.targetMs, 1_000, 100, 60_000);
  const runId = safePerfRunId();
  const dir = "/root/.unidesk/.state/code-queue-perf";
  const browsersPath = "/root/.unidesk/.state/playwright-browsers";
  const outputPath = `${dir}/${runId}.json`;
  const stderrPath = `${dir}/${runId}.stderr`;
  const exitPath = `${dir}/${runId}.exit`;
  const urlArg = typeof body.url === "string" && body.url.length > 0 ? ` --url ${shellQuote(body.url)}` : "";
  const chromePath = `${browsersPath}/chromium-1217/chrome-linux64/chrome`;
  const playwrightSetup = [
    `if ! test -x ${shellQuote(chromePath)}; then PLAYWRIGHT_BROWSERS_PATH=${shellQuote(browsersPath)} npx playwright install chromium; fi`,
    `if test -x ${shellQuote(chromePath)} && ldd ${shellQuote(chromePath)} 2>&1 | grep -q 'not found'; then npx playwright install-deps chromium; fi`,
  ].join(" && ");
  const startCommand = [
    `mkdir -p ${shellQuote(dir)}`,
    `rm -f ${shellQuote(outputPath)} ${shellQuote(stderrPath)} ${shellQuote(exitPath)}`,
    `(${playwrightSetup}; PLAYWRIGHT_BROWSERS_PATH=${shellQuote(browsersPath)} bun scripts/src/code-queue-perf.ts --json --timeout-ms ${timeoutMs} --target-ms ${targetMs}${urlArg} > ${shellQuote(outputPath)}; printf '%s' "$?" > ${shellQuote(exitPath)}) > ${shellQuote(stderrPath)} 2>&1 & printf '%s\\n' ${shellQuote(runId)}`,
  ].join("; ");
  const startedAt = Date.now();
  const start = await runHostSshPerfCommand(providerId, startCommand);
  if (!start.ok) {
    return jsonResponse({ ok: true, measurementOk: false, providerId, runId, stage: "start", error: start.stderr || "failed to start Playwright benchmark", taskId: start.taskId, task: rawTaskJson(start.task) }, 200);
  }

  const pollCommand = [
    `if test -f ${shellQuote(exitPath)}; then`,
    "printf '__UNIDESK_CODEX_QUEUE_PERF_READY__\\n';",
    `cat ${shellQuote(exitPath)};`,
    "printf '\\n__UNIDESK_CODEX_QUEUE_PERF_STDOUT__\\n';",
    `cat ${shellQuote(outputPath)} 2>/dev/null || true;`,
    "printf '\\n__UNIDESK_CODEX_QUEUE_PERF_STDERR__\\n';",
    `tail -c 3000 ${shellQuote(stderrPath)} 2>/dev/null || true;`,
    "else printf '__UNIDESK_CODEX_QUEUE_PERF_PENDING__\\n'; fi",
  ].join(" ");
  let latestPoll: Awaited<ReturnType<typeof runHostSshPerfCommand>> | null = null;
  while (Date.now() - startedAt < timeoutMs + 20_000) {
    await Bun.sleep(1_500);
    latestPoll = await runHostSshPerfCommand(providerId, pollCommand);
    const parsedPoll = parsePerfPoll(latestPoll.stdout);
    if (!parsedPoll.ready) continue;
    const result = parsePerfJson(parsedPoll.body);
    if (result === null) {
      return jsonResponse({
        ok: true,
        measurementOk: false,
        providerId,
        runId,
        stage: "parse",
        exitCode: parsedPoll.exitCode,
        error: "Playwright benchmark did not emit JSON",
        stderr: parsedPoll.stderr,
        taskId: latestPoll.taskId,
      }, 200);
    }
    return jsonResponse({
      ok: true,
      measurementOk: result.ok === true,
      providerId,
      runId,
      taskId: latestPoll.taskId,
      elapsedMs: Date.now() - startedAt,
      exitCode: parsedPoll.exitCode,
      result,
      stderr: parsedPoll.stderr,
    });
  }

  return jsonResponse({
    ok: true,
    measurementOk: false,
    providerId,
    runId,
    stage: "timeout",
    elapsedMs: Date.now() - startedAt,
    error: `Code Queue Playwright benchmark did not finish within ${timeoutMs}ms`,
    latestTaskId: latestPoll?.taskId ?? start.taskId,
    latestTask: rawTaskJson(latestPoll?.task ?? start.task),
  }, 200);
}
