// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. entry 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:3410-3600 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 { deploySshIdentityPlan, 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 { cleanupFailedPods, cleanupRuns, status } from "./cleanup";
import { makeRunId, resolveDeployDevManifest, runDevE2E } from "./dev-e2e";
import { ciHelp, requireRunId } from "./help";
import { install, installAsync, installStatus } from "./install";
import { logs } from "./logs";
import { blockedArtifactResult, blockedReason, boolFlag, ciCleanupFailedPodsOptions, ciCleanupRunsOptions, ciLogsOptions, isHelpArg, numberOption, publishTransportOption, requireDesiredRef, requireFullCommit, requireRepoRelativePath, requireRevision, requireServiceId, resolveCatalogArtifact, stringOption, userServicePublishBoundaryBlock } from "./options";
import { backendCoreArtifactSourceHostPath, userServiceArtifactSourceHostPath } from "./pipelinerun";
import { publishBackendCoreArtifact, publishUserServiceArtifact, run } from "./publish";
import { ciTarget, ciTargetSourceSummary, providerIdOption } from "./types";

export async function runCiCommand(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown>> {
  const [action = "status", nameArg] = args;
  if (isHelpArg(action) || args.slice(1).some(isHelpArg)) return ciHelp();
  if (action === "install") {
    const options = {
      target: ciTarget(providerIdOption(args)),
      skipPrewarm: boolFlag(args, "--skip-prewarm"),
      skipTektonInstall: boolFlag(args, "--skip-tekton-install"),
      wait: boolFlag(args, "--wait"),
    };
    return options.wait ? install(config, options) : installAsync(options);
  }
  if (action === "install-status") return installStatus(nameArg ?? "latest");
  if (action === "plan") {
    const target = ciTarget(providerIdOption(args));
    return {
      ok: true,
      action: "ci-plan",
      mutation: false,
      target: ciTargetSourceSummary(target),
      configTruth: target.configSource.configPath,
      next: {
        status: `bun scripts/cli.ts ci status --target ${target.targetId}`,
        installDryRun: `bun scripts/cli.ts ci install --target ${target.targetId} --skip-prewarm --skip-tekton-install`,
        run: `bun scripts/cli.ts ci run --target ${target.targetId} --revision <commit>`,
      },
    };
  }
  if (action === "status") return status(ciTarget(providerIdOption(args)));
  if (action === "github-ssh-identity") {
    const subAction = nameArg ?? "plan";
    const target = providerIdOption(args);
    const identityId = stringOption(args, "--identity") ?? stringOption(args, "--identity-id");
    if (subAction === "plan") return deploySshIdentityPlan(target, identityId);
    if (subAction === "ensure") {
      if (!boolFlag(args, "--confirm")) {
        const confirmArgs = [
          "bun scripts/cli.ts ci github-ssh-identity ensure",
          target === null ? "" : `--target ${target}`,
          identityId === null ? "" : `--identity ${identityId}`,
          "--confirm",
        ].filter((item) => item.length > 0).join(" ");
        return {
          ok: false,
          action: "deploy-ssh-identity-ensure",
          mutation: false,
          mode: "confirm-required",
          plan: deploySshIdentityPlan(target, identityId),
          next: {
            confirm: confirmArgs,
          },
        };
      }
      return await ensureGithubSshIdentityForProvider(config, target ?? "", identityId);
    }
    throw new Error("ci github-ssh-identity must be one of: plan, ensure");
  }
  if (action === "run") {
    const target = ciTarget(providerIdOption(args));
    const repoUrl = stringOption(args, "--repo") ?? stringOption(args, "--repo-url") ?? "https://github.com/pikasTech/unidesk";
    const revision = requireRevision(stringOption(args, "--revision") ?? stringOption(args, "--commit"));
    const waitMs = numberOption(args, "--wait-ms", 0);
    return run({ repoUrl, revision, waitMs, target });
  }
  if (action === "publish-backend-core") {
    if (stringOption(args, "--repo") !== null || stringOption(args, "--repo-url") !== null) {
      throw new Error("ci publish-backend-core reads source repo from CI.json; edit CI.json instead of using --repo");
    }
    const commit = requireFullCommit(stringOption(args, "--commit") ?? stringOption(args, "--revision"));
    const waitMs = numberOption(args, "--wait-ms", 0);
    const dryRun = boolFlag(args, "--dry-run");
    const artifact = resolveCatalogArtifact("backend-core");
    if (artifact.kind !== "source-build") throw new Error("backend-core must be modeled as a source-build artifact in CI.json");
    if (artifact.status === "blocked") return blockedArtifactResult(artifact, commit, blockedReason(artifact));
    return publishBackendCoreArtifact(config, {
      repoUrl: artifact.source.repo,
      commit,
      waitMs,
      sourceHostPath: backendCoreArtifactSourceHostPath(commit),
      dockerfile: artifact.source.dockerfile,
      imageRepository: artifact.image.repository,
      dryRun,
    });
  }
  if (action === "publish-user-service") {
    const serviceId = requireServiceId(stringOption(args, "--service") ?? stringOption(args, "--service-id"));
    const commit = requireFullCommit(stringOption(args, "--commit") ?? stringOption(args, "--revision"));
    const waitMs = numberOption(args, "--wait-ms", 0);
    const dryRun = boolFlag(args, "--dry-run");
    const transport = publishTransportOption(stringOption(args, "--transport"));
    if (stringOption(args, "--repo") !== null || stringOption(args, "--repo-url") !== null) {
      throw new Error("ci publish-user-service reads source repo from CI.json; edit CI.json instead of using --repo");
    }
    const artifact = resolveCatalogArtifact(serviceId);
    if (artifact.kind === "source-build" && artifact.serviceId === "backend-core") {
      throw new Error("backend-core uses ci publish-backend-core; publish-user-service is for registered user services");
    }
    if (artifact.kind === "upstream-image") {
      return blockedArtifactResult(artifact, commit, artifact.blockedReason);
    }
    if (artifact.status === "blocked") {
      return blockedArtifactResult(artifact, commit, blockedReason(artifact));
    }
    const repoUrl = artifact.source.repo;
    const dockerfile = requireRepoRelativePath(artifact.source.dockerfile, `CI.json.artifacts.${serviceId}.source.dockerfile`);
    const boundaryBlock = userServicePublishBoundaryBlock(config, serviceId, commit, artifact);
    if (boundaryBlock !== null) return boundaryBlock;
    return publishUserServiceArtifact(config, {
      repoUrl,
      commit,
      waitMs,
      serviceId,
      dockerfile,
      imageRepository: artifact.image.repository,
      sourceHostPath: userServiceArtifactSourceHostPath(serviceId, commit),
      dryRun,
      transport,
    });
  }
  if (action === "run-dev-e2e") {
    const repoUrl = stringOption(args, "--repo") ?? stringOption(args, "--repo-url") ?? "https://github.com/pikasTech/unidesk";
    const desiredRef = requireDesiredRef(stringOption(args, "--desired-ref") ?? stringOption(args, "--deploy-branch"));
    const manifest = resolveDeployDevManifest(desiredRef);
    const waitMs = numberOption(args, "--wait-ms", 0);
    const runId = requireRunId(stringOption(args, "--run-id") ?? makeRunId(manifest.deployCommit));
    return runDevE2E({
      repoUrl,
      desiredRef,
      deployCommit: manifest.deployCommit,
      environment: manifest.environment,
      scriptRepo: manifest.ci.repo,
      scriptPath: manifest.ci.scriptPath,
      scriptTimeoutMs: manifest.ci.timeoutMs,
      services: manifest.services,
      runId,
      keepNamespace: boolFlag(args, "--keep-namespace"),
      waitMs,
    });
  }
  if (action === "logs") return logs(config, nameArg ?? "", ciTarget(providerIdOption(args)), ciLogsOptions(args));
  if (action === "cleanup-runs") return cleanupRuns(config, ciCleanupRunsOptions(args));
  if (action === "cleanup-failed-pods") return cleanupFailedPods(config, ciCleanupFailedPodsOptions(args));
  throw new Error("ci command must be one of: install, plan, status, run, publish-backend-core, publish-user-service, run-dev-e2e, logs, cleanup-runs, cleanup-failed-pods");
}

export function startCiInstallJob(targetId?: string): Record<string, unknown> {
  return installAsync({
    target: ciTarget(targetId ?? null),
    skipPrewarm: false,
    skipTektonInstall: false,
    wait: false,
  });
}
