// Telegram package Docker harness.
// Runs QA live transport code against the package candidate installed in Docker.

import { randomUUID } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
import type { QaProviderMode } from "../../extensions/qa-lab/src/run-config.ts";
import type { QaSuiteRoundTripProbe } from "../../extensions/qa-lab/src/suite-round-trip.ts";

function parseBoolean(value: string | undefined) {
  const normalized = value?.trim().toLowerCase();
  return normalized === "1" || normalized === "true" || normalized === "yes";
}

function splitCsv(value: string | undefined) {
  return (value ?? "")
    .split(",")
    .map((entry) => entry.trim())
    .filter((entry) => entry.length > 0);
}

function parsePositiveIntegerEnv(env: NodeJS.ProcessEnv, name: string) {
  const raw = env[name]?.trim();
  if (!raw) {
    return undefined;
  }
  if (!/^\d+$/u.test(raw)) {
    throw new Error(`invalid ${name}: ${raw}`);
  }
  const value = Number(raw);
  if (!Number.isSafeInteger(value) || value <= 0) {
    throw new Error(`invalid ${name}: ${raw}`);
  }
  return value;
}

function resolveCredentialSource(env: NodeJS.ProcessEnv) {
  return env.OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE ?? env.OPENCLAW_QA_CREDENTIAL_SOURCE;
}

function resolveCredentialRole(env: NodeJS.ProcessEnv) {
  return env.OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE ?? env.OPENCLAW_QA_CREDENTIAL_ROLE;
}

function createRunId() {
  return `${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
}

function resolvePackageTelegramOutputDir(env: NodeJS.ProcessEnv, repoRoot: string) {
  return (
    env.OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR?.trim() ||
    path.join(repoRoot, ".artifacts", "qa-e2e", `npm-telegram-live-${createRunId()}`)
  );
}

const DEFAULT_RTT_CHECK_ID = "channel-canary";

function resolveRttOptions(env: NodeJS.ProcessEnv, selectedScenarioIds: readonly string[] = []) {
  const explicitCheckIds = splitCsv(env.OPENCLAW_NPM_TELEGRAM_RTT_CHECKS);
  const checkIds = explicitCheckIds.length > 0 ? explicitCheckIds : [DEFAULT_RTT_CHECK_ID];
  const unknownCheckIds = checkIds.filter((checkId) => checkId !== DEFAULT_RTT_CHECK_ID);
  if (unknownCheckIds.length > 0) {
    throw new Error(`unknown Telegram QA RTT check: ${unknownCheckIds[0]}`);
  }
  if (
    explicitCheckIds.length === 0 &&
    selectedScenarioIds.length > 0 &&
    !selectedScenarioIds.includes(DEFAULT_RTT_CHECK_ID)
  ) {
    return undefined;
  }
  const count = parsePositiveIntegerEnv(env, "OPENCLAW_NPM_TELEGRAM_RTT_SAMPLES") ?? 20;
  return {
    scenarioId: DEFAULT_RTT_CHECK_ID,
    count,
    timeoutMs: parsePositiveIntegerEnv(env, "OPENCLAW_NPM_TELEGRAM_RTT_TIMEOUT_MS") ?? 30_000,
    maxFailures: parsePositiveIntegerEnv(env, "OPENCLAW_NPM_TELEGRAM_RTT_MAX_FAILURES") ?? count,
  };
}

function createRoundTripProbe(
  options: ReturnType<typeof resolveRttOptions>,
): QaSuiteRoundTripProbe | undefined {
  if (!options) {
    return undefined;
  }
  return {
    ...options,
    markerPrefix: "QA-TELEGRAM-RTT",
    input: {
      conversation: { id: "telegram-rtt-room", kind: "group" },
      senderId: "qa-rtt-driver",
      senderName: "QA RTT Driver",
    },
    textPrefix: "@openclaw Telegram RTT check. Reply exactly: ",
    chainReplies: true,
  };
}

async function shouldFailPackageTelegramRun(
  result: { summaryPath: string },
  env: NodeJS.ProcessEnv = process.env,
) {
  if (parseBoolean(env.OPENCLAW_NPM_TELEGRAM_ALLOW_FAILURES)) {
    return false;
  }
  const { readQaSuiteFailedOrSkippedScenarioCountFromFile } =
    await import("../../extensions/qa-lab/src/suite-summary.ts");
  return (await readQaSuiteFailedOrSkippedScenarioCountFromFile(result.summaryPath)) > 0;
}

async function resolveTrustedOpenClawCommand(
  rawCommand: string,
  env: NodeJS.ProcessEnv = process.env,
) {
  if (!path.isAbsolute(rawCommand)) {
    throw new Error("OPENCLAW_NPM_TELEGRAM_SUT_COMMAND must be an absolute path.");
  }
  const commandName = path.basename(rawCommand);
  if (commandName !== "openclaw" && commandName !== "openclaw.cmd") {
    throw new Error(
      `OPENCLAW_NPM_TELEGRAM_SUT_COMMAND must point to openclaw; got: ${commandName}`,
    );
  }
  const npmPrefix = env.NPM_CONFIG_PREFIX?.trim();
  if (!npmPrefix) {
    throw new Error("Missing NPM_CONFIG_PREFIX for installed openclaw command validation.");
  }
  const [realCommand, realPrefix] = await Promise.all([
    fs.realpath(rawCommand),
    fs.realpath(npmPrefix),
  ]);
  if (realCommand !== realPrefix && !realCommand.startsWith(`${realPrefix}${path.sep}`)) {
    throw new Error("OPENCLAW_NPM_TELEGRAM_SUT_COMMAND must resolve inside NPM_CONFIG_PREFIX.");
  }
  return {
    executablePath: rawCommand,
    usePackagedPlugins: true,
  } as const;
}

async function main() {
  const { runQaTelegramSuite } =
    await import("../../extensions/qa-lab/src/live-transports/telegram/cli.runtime.ts");
  const rawSutOpenClawCommand = process.env.OPENCLAW_NPM_TELEGRAM_SUT_COMMAND?.trim();
  if (!rawSutOpenClawCommand) {
    throw new Error("Missing OPENCLAW_NPM_TELEGRAM_SUT_COMMAND.");
  }
  const sutOpenClawCommand = await resolveTrustedOpenClawCommand(rawSutOpenClawCommand);

  const repoRoot = path.resolve(process.env.OPENCLAW_NPM_TELEGRAM_REPO_ROOT ?? process.cwd());
  const outputDir = resolvePackageTelegramOutputDir(process.env, repoRoot);
  const scenarioIds = splitCsv(process.env.OPENCLAW_NPM_TELEGRAM_SCENARIOS);
  const result = await runQaTelegramSuite({
    allowFailures: true,
    failFast: true,
    repoRoot,
    outputDir,
    sutOpenClawCommand,
    providerMode: process.env.OPENCLAW_NPM_TELEGRAM_PROVIDER_MODE as QaProviderMode | undefined,
    primaryModel: process.env.OPENCLAW_NPM_TELEGRAM_MODEL,
    alternateModel: process.env.OPENCLAW_NPM_TELEGRAM_ALT_MODEL,
    fastMode: parseBoolean(process.env.OPENCLAW_NPM_TELEGRAM_FAST),
    scenarioIds,
    roundTripProbe: createRoundTripProbe(resolveRttOptions(process.env, scenarioIds)),
    sutAccountId: process.env.OPENCLAW_NPM_TELEGRAM_SUT_ACCOUNT,
    credentialSource: resolveCredentialSource(process.env),
    credentialRole: resolveCredentialRole(process.env),
  });
  if (!result) {
    throw new Error("Package Telegram QA did not produce suite artifacts.");
  }

  process.stdout.write(`Package Telegram QA report: ${result.reportPath}\n`);
  process.stdout.write(`Package Telegram QA summary: ${result.summaryPath}\n`);
  if (await shouldFailPackageTelegramRun(result)) {
    process.exitCode = 1;
  }
}

async function formatRunnerErrorMessage(error: unknown) {
  try {
    // Widen the specifier so the source-only test-root program does not try to
    // resolve dist (TS2307); the docker-e2e boundary guard requires importing
    // built dist here, so the cast stays structural instead of a src reference.
    const distErrorsPath = "../../dist/infra/errors.js" as string;
    const { formatErrorMessage } = (await import(distErrorsPath)) as {
      formatErrorMessage: (err: unknown) => string;
    };
    return formatErrorMessage(error);
  } catch {
    return error instanceof Error ? error.message : String(error);
  }
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  main().catch(async (error: unknown) => {
    process.stderr.write(
      `package telegram live e2e failed: ${await formatRunnerErrorMessage(error)}\n`,
    );
    process.exitCode = 1;
  });
}

export const testing = {
  parsePositiveIntegerEnv,
  resolvePackageTelegramOutputDir,
  resolveCredentialRole,
  resolveCredentialSource,
  createRoundTripProbe,
  resolveRttOptions,
  resolveTrustedOpenClawCommand,
  shouldFailPackageTelegramRun,
};
