// SPEC: PJ2026-01060703 CI/CD branch follower native Kubernetes helpers.
// Responsibility: submit/probe Kubernetes Jobs and Tekton PipelineRuns via file-backed native scripts.
import { repoRoot, rootPath } from "./config";
import { runCommand, type CommandResult } from "./command";
import type { NativeK8sJobResult } from "./cicd-types";

const NATIVE_SCRIPT_DIR = "scripts/native/cicd";

interface NativeTimingOptions {
  nativeTransportGraceSeconds: number;
  nativePollIntervalSeconds: number;
}

export function runNativeTektonPipelineRun(namespace: string, pipelineRun: string, manifest: Record<string, unknown>, wait: boolean, timeoutSeconds: number, timing: NativeTimingOptions): CommandResult {
  return runCommand(["node", rootPath(NATIVE_SCRIPT_DIR, "submit-pipelinerun.mjs")], repoRoot, {
    input: Buffer.from(JSON.stringify(manifest), "utf8").toString("base64"),
    timeoutMs: (timeoutSeconds + timing.nativeTransportGraceSeconds) * 1000,
    env: {
      ...process.env,
      NAMESPACE: namespace,
      PIPELINERUN: pipelineRun,
      WAIT: wait ? "true" : "false",
      TIMEOUT_SECONDS: String(timeoutSeconds),
      POLL_INTERVAL_SECONDS: String(timing.nativePollIntervalSeconds),
    },
  });
}

export function runNativeK8sJob(namespace: string, jobName: string, manifest: Record<string, unknown>, timeoutSeconds: number, logContainer: string, timing: NativeTimingOptions): NativeK8sJobResult {
  const result = runCommand(["node", rootPath(NATIVE_SCRIPT_DIR, "native-job.mjs")], repoRoot, {
    input: Buffer.from(JSON.stringify(manifest), "utf8").toString("base64"),
    timeoutMs: (timeoutSeconds + timing.nativeTransportGraceSeconds) * 1000,
    env: {
      ...process.env,
      NAMESPACE: namespace,
      JOB_NAME: jobName,
      LOG_CONTAINER: logContainer,
      TIMEOUT_SECONDS: String(timeoutSeconds),
      POLL_INTERVAL_SECONDS: String(timing.nativePollIntervalSeconds),
    },
  });
  const parsed = parseJsonObject(result.stdout);
  const nativeWaitTimedOut = parsed?.timedOut === true && parsed?.failed !== true;
  return {
    ok: result.exitCode === 0 && parsed?.ok === true,
    completed: parsed?.completed === true,
    failed: parsed?.failed === true || (result.exitCode !== 0 && !nativeWaitTimedOut),
    timedOut: nativeWaitTimedOut || result.timedOut,
    created: parsed?.created === true,
    reused: parsed?.reused === true,
    jobName,
    namespace,
    polls: numberOrNull(parsed?.polls) ?? 0,
    elapsedMs: numberOrNull(parsed?.elapsedMs) ?? 0,
    startedAt: stringOrNull(parsed?.startedAt),
    finishedAt: stringOrNull(parsed?.finishedAt),
    logsTail: stringOrNull(parsed?.logsTail),
    summary: asOptionalRecord(parsed?.summary),
    conditionReason: stringOrNull(parsed?.conditionReason),
    conditionMessage: stringOrNull(parsed?.conditionMessage) ?? (result.exitCode === 0 || nativeWaitTimedOut ? null : tailText(result.stderr || result.stdout, 500)),
    statusAuthority: "kubernetes-api-serviceaccount",
    parsedDownstreamCliOutput: false,
  };
}

function parseJsonObject(text: string): Record<string, unknown> | null {
  const trimmed = text.trim();
  if (trimmed.length === 0) return null;
  try {
    const parsed = JSON.parse(trimmed) as unknown;
    return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed as Record<string, unknown> : null;
  } catch {
    return null;
  }
}

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

function stringOrNull(value: unknown): string | null {
  return typeof value === "string" && value.length > 0 ? value : null;
}

function numberOrNull(value: unknown): number | null {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}

function tailText(text: string, maxChars: number): string {
  return text.length <= maxChars ? text : text.slice(text.length - maxChars);
}
