// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. install module for scripts/src/ci.ts.
// SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets.

// Moved mechanically from scripts/src/ci.ts:1429-1559 for #903.

import { randomUUID } from "node:crypto";
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, posix as posixPath } from "node:path";
import { blockedCatalogArtifactIds, catalogSummary, findCiCatalogArtifact, loadCiCatalog, supportedSourceBuildArtifactIds, type CiCatalogArtifact, type CiSourceBuildCatalogArtifact, type CiUpstreamImageCatalogArtifact } from "../ci-catalog";
import { runCommand } from "../command";
import { type UniDeskConfig, repoRoot, rootPath } from "../config";
import { ensureGithubSshIdentityForProvider, gitSshHttpConnectProxySource } from "../deploy-ssh-identity";
import { jobWithTail, listJobs, readJob, startJob } from "../jobs";
import { coreInternalFetch } from "../microservices";
import {
  artifactRegistryReadonlyResultFromCommand,
  buildArtifactRegistryReadonlyProbe,
  parseArtifactRegistryOptions,
  type ArtifactRegistryReadonlyProbe,
} from "../artifact-registry";
import { d601K3sGuardShellLines, d601NativeKubeconfig } from "../d601-k3s-guard";
import { runSshCommandCapture } from "../ssh";

import type { CiInstallOptions } from "./types";
import { status } from "./cleanup";
import { ciTargetGuardShellLines, shellQuote } from "./options";
import { emitCiInstallProgress } from "./preflight";
import { prewarmCiRuntimeImages, remoteApplyManifest, runRemoteBackground } from "./remote";
import { tektonPipelineReleaseUrl, tektonTriggersInterceptorsUrl, tektonTriggersReleaseUrl } from "./types";

export async function install(config: UniDeskConfig, options: CiInstallOptions): Promise<Record<string, unknown>> {
  if (!existsSync(rootPath(options.target.pipelineManifest))) {
    throw new Error("CI manifests are missing");
  }
  emitCiInstallProgress("install", "started", {
    providerId: options.target.providerId,
    skipPrewarm: options.skipPrewarm,
    skipTektonInstall: options.skipTektonInstall,
  });
  try {
    if (options.skipPrewarm) {
      emitCiInstallProgress("prewarm", "skipped", { providerId: options.target.providerId });
    } else {
      emitCiInstallProgress("prewarm", "started", { providerId: options.target.providerId });
      await prewarmCiRuntimeImages(options.target);
      emitCiInstallProgress("prewarm", "succeeded", { providerId: options.target.providerId });
    }
    if (options.skipTektonInstall) {
      emitCiInstallProgress("install-tekton", "skipped", { providerId: options.target.providerId });
    } else {
      emitCiInstallProgress("install-tekton", "started", { providerId: options.target.providerId });
      const installTektonScript = [
        "set -euo pipefail",
        ...ciTargetGuardShellLines(options.target),
        `kubectl apply -f ${shellQuote(tektonPipelineReleaseUrl)}`,
        "kubectl wait --for=condition=Available deployment --all -n tekton-pipelines --timeout=900s",
        `kubectl apply -f ${shellQuote(tektonTriggersReleaseUrl)}`,
        `kubectl apply -f ${shellQuote(tektonTriggersInterceptorsUrl)}`,
        "kubectl wait --for=condition=Available deployment --all -n tekton-pipelines --timeout=900s",
        "kubectl wait --for=condition=Available deployment --all -n tekton-pipelines-resolvers --timeout=900s",
      ].join("\n");
      const installTekton = await runRemoteBackground("install-tekton", installTektonScript, 1_200_000, options.target);
      if (!installTekton.ok) throw new Error(`Tekton install failed: ${installTekton.stderr || installTekton.stdout}`);
      emitCiInstallProgress("install-tekton", "succeeded", { providerId: options.target.providerId });
    }
    for (const manifest of [
      "src/components/microservices/k3sctl-adapter/k3s/ci/tekton-install.yaml",
      options.target.pipelineManifest,
      "src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.triggers.yaml",
    ]) {
      emitCiInstallProgress("apply-manifest", "started", { providerId: options.target.providerId, manifest });
      await remoteApplyManifest(config, manifest, options.target);
      emitCiInstallProgress("apply-manifest", "succeeded", { providerId: options.target.providerId, manifest });
    }
    emitCiInstallProgress("status", "started", { providerId: options.target.providerId });
    const summary = await status(options.target);
    emitCiInstallProgress("status", "succeeded", { providerId: options.target.providerId });
    emitCiInstallProgress("install", "succeeded", { providerId: options.target.providerId });
    return {
      ...summary,
      install: {
        providerId: options.target.providerId,
        prewarm: options.skipPrewarm ? "skipped" : "completed",
        tektonInstall: options.skipTektonInstall ? "skipped" : "completed",
      },
    };
  } catch (error) {
    emitCiInstallProgress("install", "failed", {
      providerId: options.target.providerId,
      errorTail: (error instanceof Error ? error.message : String(error)).slice(-1200),
    });
    throw error;
  }
}

export function ciInstallCommand(options: CiInstallOptions): string[] {
  return [
    "bun",
    "scripts/cli.ts",
    "ci",
    "install",
    "--target",
    options.target.targetId,
    "--wait",
    ...(options.skipPrewarm ? ["--skip-prewarm"] : []),
    ...(options.skipTektonInstall ? ["--skip-tekton-install"] : []),
  ];
}

export function installAsync(options: CiInstallOptions): Record<string, unknown> {
  const command = ciInstallCommand(options);
  const job = startJob(
    "ci_install",
    command,
    `Install/refresh Tekton CI on ${options.target.providerId} native k3s`,
  );
  return {
    ok: true,
    mode: "async",
    providerId: options.target.providerId,
    skipped: {
      prewarm: options.skipPrewarm,
      tektonInstall: options.skipTektonInstall,
    },
    job: {
      id: job.id,
      status: job.status,
      command: job.command,
      stdoutFile: job.stdoutFile,
      stderrFile: job.stderrFile,
      note: job.note,
    },
    next: [
      `bun scripts/cli.ts ci install-status ${job.id}`,
      `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`,
    ],
    boundary: "ci install is fire-and-forget by default; use --wait only for explicit synchronous debugging.",
  };
}

export function latestCiInstallJobId(): string {
  const job = listJobs().find((item) => item.name === "ci_install");
  if (job === undefined) throw new Error("no ci_install job found");
  return job.id;
}

export function installStatus(id: string): Record<string, unknown> {
  const jobId = id === "latest" || id.length === 0 ? latestCiInstallJobId() : id;
  const job = readJob(jobId);
  if (job.name !== "ci_install") {
    throw new Error(`job ${jobId} is ${job.name}, not ci_install`);
  }
  return {
    ok: job.status !== "failed" && job.status !== "canceled",
    job: jobWithTail(job, 12_000),
    next: job.status === "running" || job.status === "queued"
      ? [`bun scripts/cli.ts ci install-status ${job.id}`]
      : ["bun scripts/cli.ts ci status"],
  };
}
