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

// Moved mechanically from scripts/src/deploy.ts:3528-3700 for #903.

import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { runCommand } from "../command";
import { type UniDeskConfig, type UniDeskMicroserviceConfig, repoRoot, rootPath } from "../config";
import { ensureGithubSshIdentityForProvider } from "../deploy-ssh-identity";
import { baiduNetdiskRuntimeSecretRequirements, runtimeSecretContractFromEnvText, type RuntimeSecretContract, runArtifactRegistryCommand } from "../artifact-registry";
import { startJob } from "../jobs";
import { coreInternalFetch } from "../microservices";
import { codeQueueSourceImportPreflight, codeQueueSourceSubdir } from "../code-queue-source-guard";
import { d601K3sGuardShellLines, d601NativeKubeconfig } from "../d601-k3s-guard";
import { composeRuntimeEnvValue } from "../runtime-env";
import {
  compareDeployJsonExecutorMirrors,
  deployJsonCommitImage,
  deployJsonDriftResult,
  deployJsonSourceOfTruth,
  encodeDeployJsonServiceContract,
  hasDeployJsonExecutorContract,
  k3sManifestExecutorMirror,
  parseDeployJsonServiceContract,
  type DeployJsonExecutorMirror,
  type DeployJsonServiceContract,
} from "../deploy-json-contract";
import { resolveArtifactRegistryTarget } from "../ops/targets";

import type { DeployAction } from "./types";
import { applyJob, devArtifactApplyJob, prodArtifactApplyJob, runApplyNow, runArtifactConsumerApplyNow, runProdArtifactApplyNow } from "./artifact-jobs";
import { blockedD601MaintenanceDeployServices, checkOrPlan, commitOverrideArtifactConsumerError, d601MaintenanceDeployBlockMessage, devArtifactLiveApplyBlockedResult, environmentDryRunPlan, prodArtifactConsumerLocalManifestResult, prodArtifactUnsupportedResult, prodArtifactUnsupportedServices, selectedDevArtifactServicesWithProdFallback, selectedDevTargetServices, selectedEnvironmentServices, unsupportedDevApplyServices } from "./check-plan";
import { readDeployManifest, readEnvironmentDeployManifest } from "./manifest";
import { deployHelp, isHelpArg, optionValue, parseOptions, resolveManifestCommits } from "./options";
import { devArtifactLiveApplyBlockedServiceIds, prodForbiddenTargetSideBuildServiceIds } from "./types";

export function runDeployGuardCommand(args: string[]): unknown {
  const [guardName] = args;
  if (guardName !== "code-queue-source") {
    return {
      ok: false,
      supported: false,
      error: "unsupported-deploy-guard",
      guard: guardName ?? null,
      supportedGuards: ["code-queue-source"],
    };
  }
  const root = optionValue(args.slice(1), ["--root"]) ?? repoRoot;
  return codeQueueSourceImportPreflight(root);
}

export async function runDeployCommand(config: UniDeskConfig | null, args: string[]): Promise<unknown> {
  const [actionRaw = "check"] = args;
  if (isHelpArg(actionRaw) || args.slice(1).some(isHelpArg)) return deployHelp(isHelpArg(actionRaw) ? undefined : actionRaw);
  if (actionRaw === "guard") return runDeployGuardCommand(args.slice(1));
  if (!["check", "plan", "apply"].includes(actionRaw)) throw new Error("deploy command must be one of: check, plan, apply, guard");
  const action = actionRaw as DeployAction;
  const options = parseOptions(args.slice(1));
  if (options.environment !== null) {
    const commitOverrideError = commitOverrideArtifactConsumerError(options);
    if (commitOverrideError !== null) throw new Error(commitOverrideError);
    const { manifest, source } = readEnvironmentDeployManifest(options.environment);
    if (action === "check" || action === "plan") return environmentDryRunPlan(config, manifest, source, options, action);
    if (options.environment === "prod") {
      const unsupported = prodArtifactUnsupportedServices(manifest, options.serviceId);
      if (unsupported.length > 0) return prodArtifactUnsupportedResult(unsupported);
      if (options.dryRun || options.runNow) return await runProdArtifactApplyNow(manifest, options);
      return prodArtifactApplyJob(args, options);
    }
    const unsupported = unsupportedDevApplyServices(manifest, options.serviceId);
    if (unsupported.length > 0) {
      throw new Error(`deploy apply --env dev currently supports auth-broker/backend-core/frontend/baidu-netdisk/decision-center/mdtodo/claudeqq/code-queue/project-manager/oa-event-flow/code-queue-mgr/todo-note/findjob/pipeline/met-nonlinear artifact consumers; unsupported selected services: ${unsupported.join(", ")}. Use ci run-dev-e2e for smoke verification.`);
    }
    const devArtifactServices = selectedDevArtifactServicesWithProdFallback(manifest, options.serviceId);
    const devTargetServices = selectedDevTargetServices(manifest, options.serviceId);
    if (devArtifactServices.length > 0 && devTargetServices.length > 0) {
      throw new Error("deploy apply --env dev cannot mix artifact consumer services with target-side rollout services in one invocation; pass --service");
    }
    if (devArtifactServices.length > 0) {
      const blocked = devArtifactServices.filter((service) => devArtifactLiveApplyBlockedServiceIds.has(service.id));
      if (!options.dryRun && blocked.length > 0) return devArtifactLiveApplyBlockedResult(blocked);
      if (options.dryRun || options.runNow) return await runArtifactConsumerApplyNow(manifest, options, "dev", devArtifactServices);
      return devArtifactApplyJob(args, options);
    }
    if (config === null) throw new Error("deploy apply --env dev requires config.json");
    if (!options.dryRun) {
      const blocked = blockedD601MaintenanceDeployServices(config, manifest, options.serviceId).filter((serviceId) => serviceId !== "decision-center");
      if (blocked.length > 0) throw new Error(d601MaintenanceDeployBlockMessage(blocked));
    }
    if (options.dryRun || options.runNow) return await runApplyNow(config, manifest, options);
    return applyJob(config, args, options);
  }
  if (config === null) throw new Error("deploy local manifest mode requires config.json");
  const rawManifest = await readDeployManifest(options.file);
  if (action === "apply") {
    const blocked = blockedD601MaintenanceDeployServices(config, rawManifest, options.serviceId);
    if (blocked.length > 0) throw new Error(d601MaintenanceDeployBlockMessage(blocked));
  }
  const manifest = resolveManifestCommits(rawManifest, options.serviceId);
  if (action === "check" || action === "plan") return await checkOrPlan(config, manifest, options, action);
  const prodArtifactConsumers = selectedEnvironmentServices(manifest, options.serviceId)
    .filter((service) => manifest.environment !== "dev" && prodForbiddenTargetSideBuildServiceIds.has(service.id));
  if (prodArtifactConsumers.length > 0) return prodArtifactConsumerLocalManifestResult(prodArtifactConsumers);
  if (options.dryRun || options.runNow) return await runApplyNow(config, manifest, options);
  return applyJob(config, args, options);
}

export async function runCodeQueueDeployCompatCommand(_config: UniDeskConfig, args: string[]): Promise<unknown> {
  if (args.includes("--skip-build")) throw new Error("codex deploy is disabled; --skip-build is not supported");
  const selectedTarget = optionValue(args, ["--target", "--provider-id", "--provider"]);
  if (selectedTarget === undefined) throw new Error("codex deploy compatibility path is a legacy adapter and requires explicit --target D601; it no longer falls back to a hidden provider default");
  const target = resolveArtifactRegistryTarget(selectedTarget);
  if (target.providerId !== "D601") throw new Error(`codex deploy compatibility path only supports D601; got ${target.providerId}`);
  throw new Error("codex deploy is disabled because D601 maintenance-channel direct deployment must not deploy Code Queue. Use the dev-only artifact consumer with deploy apply --env dev --service code-queue or artifact-registry deploy-service --env dev --service code-queue; production Code Queue artifact deployment is unsupported.");
}
