// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. service-plan 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:634-1236 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 type { ArtifactConsumerPlanKind, DeployEnvironment, DeployEnvironmentTarget, DeployManifest, DeployManifestService } from "./types";
import { targetIsMain, targetWorkDir } from "./paths";
import { artifactConsumerDryRunBlockedServiceIds, d601DeployNodeId, d601DeployProviderId, d601MaintenanceDeployAllowedServiceIds, devApplySupportedServiceIds, devArtifactConsumerServiceIds, k8sNamespace, prodArtifactConsumerServiceIds, prodArtifactLiveApplyBlockedServiceIds, unideskRepoUrl } from "./types";

export function frontendCoreDeployService(config: UniDeskConfig): UniDeskMicroserviceConfig {
  return {
    id: "frontend",
    name: "UniDesk Frontend",
    providerId: "main-server",
    description: "UniDesk core frontend entrypoint deployed by the main-server direct Compose executor.",
    repository: {
      url: unideskRepoUrl,
      commitId: "local",
      dockerfile: "src/components/frontend/Dockerfile",
      composeFile: config.docker.composeFile,
      composeService: "frontend",
      containerName: "unidesk-frontend",
    },
    backend: {
      nodeBaseUrl: `http://127.0.0.1:${config.network.frontend.port}`,
      nodeBindHost: "127.0.0.1",
      nodePort: config.network.frontend.port,
      proxyMode: "core-direct",
      frontendOnly: false,
      public: true,
      allowedMethods: ["GET", "HEAD"],
      allowedPathPrefixes: ["/"],
      healthPath: "/health",
      timeoutMs: 8000,
    },
    deployment: { mode: "unidesk-direct" },
    development: {
      providerId: "main-server",
      sshPassthrough: false,
      worktreePath: repoRoot,
    },
    frontend: {
      route: "/",
      integrated: true,
    },
  };
}

export function devK3sDeployService(id: string): UniDeskMicroserviceConfig | undefined {
  const specs: Record<string, {
    name: string;
    description: string;
    repoUrl?: string;
    dockerfile: string;
    composeService: string;
    composeFile: string;
    containerName: string;
    nodeBaseUrl: string;
    nodePort: number;
    healthPath: string;
    route: string;
    allowedMethods: string[];
    allowedPathPrefixes: string[];
  }> = {
    "backend-core": {
      name: "UniDesk Dev Backend Core",
      description: "Isolated dev backend-core deployed into D601 native k3s namespace unidesk-dev.",
      dockerfile: "src/components/backend-core/Dockerfile",
      composeFile: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k8s.yaml",
      composeService: "backend-core-dev",
      containerName: "k3s:backend-core-dev",
      nodeBaseUrl: "k3s://backend-core-dev",
      nodePort: 8080,
      healthPath: "/health",
      route: "/dev/backend-core",
      allowedMethods: ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
      allowedPathPrefixes: ["/", "/api/", "/logs"],
    },
    frontend: {
      name: "UniDesk Dev Frontend",
      description: "Isolated dev frontend deployed into D601 native k3s namespace unidesk-dev.",
      dockerfile: "src/components/frontend/Dockerfile",
      composeFile: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k8s.yaml",
      composeService: "frontend-dev",
      containerName: "k3s:frontend-dev",
      nodeBaseUrl: "k3s://frontend-dev",
      nodePort: 8080,
      healthPath: "/health",
      route: "/dev/frontend",
      allowedMethods: ["GET", "HEAD"],
      allowedPathPrefixes: ["/"],
    },
    "decision-center": {
      name: "UniDesk Dev Decision Center",
      description: "Isolated dev Decision Center deployed into D601 native k3s namespace unidesk-dev.",
      dockerfile: "src/components/microservices/decision-center/Dockerfile",
      composeFile: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-decision-center.k8s.yaml",
      composeService: "decision-center-dev",
      containerName: "k3s:decision-center-dev",
      nodeBaseUrl: "k3s://decision-center-dev",
      nodePort: 4277,
      healthPath: "/health",
      route: "/dev/decision-center",
      allowedMethods: ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
      allowedPathPrefixes: ["/", "/api/", "/logs"],
    },
    mdtodo: {
      name: "UniDesk Dev MDTODO",
      description: "Isolated dev MDTODO deployed into D601 native k3s namespace unidesk-dev.",
      dockerfile: "src/components/microservices/mdtodo/Dockerfile",
      composeFile: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-mdtodo.k8s.yaml",
      composeService: "mdtodo-dev",
      containerName: "k3s:mdtodo-dev",
      nodeBaseUrl: "k3s://mdtodo-dev",
      nodePort: 4267,
      healthPath: "/health",
      route: "/dev/mdtodo",
      allowedMethods: ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
      allowedPathPrefixes: ["/health", "/live", "/logs", "/api/"],
    },
    claudeqq: {
      name: "UniDesk Dev ClaudeQQ",
      description: "Isolated dev ClaudeQQ deployed into D601 native k3s namespace unidesk-dev.",
      repoUrl: "https://gitee.com/lyon1998/agent_skills",
      dockerfile: "claudeqq/Dockerfile",
      composeFile: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-claudeqq.k8s.yaml",
      composeService: "claudeqq-dev",
      containerName: "k3s:claudeqq-dev",
      nodeBaseUrl: "k3s://claudeqq-dev",
      nodePort: 3290,
      healthPath: "/health",
      route: "/dev/claudeqq",
      allowedMethods: ["GET", "HEAD", "POST", "DELETE"],
      allowedPathPrefixes: ["/health", "/logs", "/api/"],
    },
    "code-queue": {
      name: "UniDesk Dev Code Queue",
      description: "Isolated dev Code Queue execution plane deployed into D601 native k3s namespace unidesk-dev.",
      dockerfile: "src/components/microservices/code-queue/Dockerfile",
      composeFile: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-code-queue.k8s.yaml",
      composeService: "code-queue-scheduler-dev",
      containerName: "k3s:code-queue-scheduler-dev",
      nodeBaseUrl: "k3s://code-queue-dev",
      nodePort: 4222,
      healthPath: "/health",
      route: "/dev/code-queue",
      allowedMethods: ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
      allowedPathPrefixes: ["/", "/api/", "/logs"],
    },
  };
  const spec = specs[id];
  if (spec === undefined) return undefined;
  return {
    id,
    name: spec.name,
    providerId: d601DeployProviderId,
    description: spec.description,
    repository: {
      url: spec.repoUrl ?? unideskRepoUrl,
      commitId: "deploy-env",
      dockerfile: spec.dockerfile,
      composeFile: spec.composeFile,
      composeService: spec.composeService,
      containerName: spec.containerName,
    },
    backend: {
      nodeBaseUrl: spec.nodeBaseUrl,
      nodeBindHost: `k3s://unidesk-dev/${spec.composeService}`,
      nodePort: spec.nodePort,
      proxyMode: "dev-k3s-direct",
      frontendOnly: true,
      public: false,
      allowedMethods: spec.allowedMethods,
      allowedPathPrefixes: spec.allowedPathPrefixes,
      healthPath: spec.healthPath,
      timeoutMs: 30_000,
    },
    deployment: {
      mode: "k3sctl-managed",
      adapterServiceId: "k3sctl-adapter",
      k3sServiceId: spec.composeService,
      namespace: "unidesk-dev",
      expectedNodeIds: [d601DeployNodeId],
      activeNodeId: d601DeployNodeId,
    },
    development: {
      providerId: d601DeployProviderId,
      sshPassthrough: true,
      worktreePath: id === "code-queue"
        ? "/home/ubuntu/unidesk-dev-code-queue-deploy/code-queue"
        : `/home/ubuntu/unidesk-dev-core-deploy/${id}`,
    },
    frontend: {
      route: spec.route,
      integrated: false,
    },
  };
}

export function coreDeployService(config: UniDeskConfig, id: string, environment: DeployEnvironment | null): UniDeskMicroserviceConfig | undefined {
  if (environment === "dev") return devK3sDeployService(id);
  if (id === "frontend") return frontendCoreDeployService(config);
  return undefined;
}

export function isCoreDeployService(service: UniDeskMicroserviceConfig): boolean {
  return service.id === "frontend" && service.providerId === "main-server" && service.backend.proxyMode === "core-direct";
}

export function isDevK3sDeployService(service: UniDeskMicroserviceConfig): boolean {
  return service.deployment.mode === "k3sctl-managed"
    && (devApplySupportedServiceIds.has(service.id) || devArtifactConsumerServiceIds.has(service.id));
}

export function isDevArtifactConsumerService(service: UniDeskMicroserviceConfig): boolean {
  return (service.deployment.mode === "k3sctl-managed" || isDirectComposeDeployMode(service))
    && devArtifactConsumerServiceIds.has(service.id);
}

export function isD601MaintenanceDeployBlocked(service: UniDeskMicroserviceConfig): boolean {
  if (targetIsMain(service)) return false;
  if (service.providerId !== "D601") return false;
  return !d601MaintenanceDeployAllowedServiceIds.has(service.id);
}

export function isDirectComposeDeployMode(service: UniDeskMicroserviceConfig): boolean {
  return service.deployment.mode === "unidesk-direct" || service.deployment.mode === "internal-sidecar";
}

export function selectServices(config: UniDeskConfig, manifest: DeployManifest, serviceId: string | null): Array<{
  desired: DeployManifestService;
  config: UniDeskMicroserviceConfig;
}> {
  const configById = new Map(config.microservices.map((service) => [service.id, service]));
  const selected = serviceId === null ? manifest.services : manifest.services.filter((service) => service.id === serviceId);
  if (serviceId !== null && selected.length === 0) throw new Error(`deploy manifest does not contain service: ${serviceId}`);
  return selected.map((desired) => {
    if (manifest.environment === "dev") {
      const service = devK3sDeployService(desired.id);
      if (service !== undefined) return { desired, config: service };
      const directService = configById.get(desired.id);
      if (directService !== undefined && isDirectComposeDeployMode(directService) && devArtifactConsumerServiceIds.has(directService.id)) {
        return { desired, config: directService };
      }
      throw new Error(`deploy --env dev service ${desired.id} is not enabled for direct rollout in the current CI-only phase`);
    }
    const service = configById.get(desired.id) ?? coreDeployService(config, desired.id, manifest.environment);
    if (service === undefined) throw new Error(`deploy manifest service ${desired.id} is not present in config.json microservices or supported core deploy services`);
    return { desired, config: service };
  });
}

export function unsupportedReason(service: UniDeskMicroserviceConfig): string | null {
  if (service.repository.dockerfile.startsWith("docker.io/")) return "image-only service has no Dockerfile source artifact";
  if (service.repository.composeFile.startsWith("docker run")) return "docker-run image-only service has no compose/k8s build path";
  if (isD601MaintenanceDeployBlocked(service)) return "D601 maintenance-channel deployment is not allowed for this service; use reviewed registry artifact consumers instead";
  if (service.repository.commitId === "local") return null;
  return null;
}

export function databaseFingerprint(target: DeployEnvironmentTarget): string {
  const material = [
    target.environment,
    target.namespace,
    target.database.serviceId,
    target.database.host,
    target.database.port.toString(),
    target.database.name,
    target.provider.providerId,
  ].join("|");
  return `sha256:${createHash("sha256").update(material).digest("hex").slice(0, 16)}`;
}

export function environmentTargetSummary(target: DeployEnvironmentTarget): Record<string, unknown> {
  return {
    namespace: target.namespace,
    runtimeScope: target.runtimeScope,
    database: {
      ...target.database,
      fingerprint: databaseFingerprint(target),
    },
    provider: target.provider,
  };
}

export function artifactConsumerPlanKind(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): ArtifactConsumerPlanKind {
  if (environment === "dev" && devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id)) {
    return "d601-dev-target-side-build";
  }
  if (service.deployment.mode === "k3sctl-managed") return "d601-k3s-managed";
  if (service.providerId === "D601" && service.deployment.mode === "unidesk-direct") return "d601-direct-compose";
  if (targetIsMain(service) && isDirectComposeDeployMode(service)) return "main-server-compose";
  return "unsupported";
}

export function artifactConsumerPlanKindFromDeployJson(service: DeployManifestService, fallback: ArtifactConsumerPlanKind): ArtifactConsumerPlanKind {
  if (service.consumer?.kind === "d601-k3s-managed") return "d601-k3s-managed";
  return fallback;
}

export function deploymentPathForEnvironmentService(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string {
  return deploymentPathForEnvironmentServiceId(service.id, environment);
}

export function deploymentPathForEnvironmentServiceId(serviceId: string, environment: DeployEnvironment): string {
  if (environment === "prod") return prodArtifactConsumerServiceIds.has(serviceId) ? "d601-registry-artifact-consumer" : "unsupported";
  if (devArtifactConsumerServiceIds.has(serviceId)) return "d601-registry-artifact-consumer";
  if (devApplySupportedServiceIds.has(serviceId)) return "d601-dev-target-side-build";
  return "unsupported";
}

export function artifactConsumerPlanTarget(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): Record<string, unknown> {
  const kind = artifactConsumerPlanKind(service, environment);
  const liveBlockReason = environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null;
  const dryRunBlockedReason = artifactConsumerDryRunBlockedServiceIds.get(service.id) ?? null;
  const targetImage = artifactConsumerTargetImageHint(service, environment);
  const common = {
    consumerKind: kind,
    runtimeHost: kind === "main-server-compose" ? "main-server" : "D601",
    deploymentMode: service.deployment.mode,
    noRuntimeSourceBuild: kind !== "d601-dev-target-side-build",
    dryRunOnly: dryRunBlockedReason !== null || (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)),
    blockedReason: dryRunBlockedReason ?? liveBlockReason,
  };
  if (kind === "d601-k3s-managed") {
    return {
      ...common,
      namespace: service.deployment.namespace ?? (environment === "dev" ? "unidesk-dev" : k8sNamespace),
      deployment: service.repository.composeService,
      service: service.deployment.k3sServiceId ?? service.repository.composeService,
      containerName: service.repository.containerName,
      manifest: service.repository.composeFile,
      targetImage,
      deployCommandShape: "registry manifest check + native k3s containerd import + Kubernetes Deployment image/env/annotation update + API service proxy health",
      forbiddenActions: ["docker build", "docker compose build", "NodePort", "hostPort", "provider-gateway direct business backend"],
    };
  }
  if (kind === "d601-direct-compose" || kind === "main-server-compose") {
    return {
      ...common,
      workDir: targetWorkDir(service),
      composeFile: service.repository.composeFile,
      composeService: service.repository.composeService,
      containerName: service.repository.containerName,
      targetImage,
      deployCommandShape: `docker compose up -d --no-build --no-deps --force-recreate ${service.repository.composeService}`,
      forbiddenActions: ["docker build", "docker compose build", "docker compose up --build", "dirty worktree source deploy"],
    };
  }
  if (kind === "d601-dev-target-side-build") {
    return {
      ...common,
      namespace: service.deployment.namespace ?? "unidesk-dev",
      deployment: service.repository.composeService,
      manifest: service.repository.composeFile,
      workDir: targetWorkDir(service),
      deployCommandShape: "D601 dev target-side source materialization/build/import/rollout",
      forbiddenActions: ["production namespace mutation", "production database mutation"],
    };
  }
  return common;
}

export function artifactConsumerPlanTargetFromDeployJson(
  service: DeployManifestService,
  fallbackService: UniDeskMicroserviceConfig,
  environment: DeployEnvironment,
  fallbackTarget: Record<string, unknown> | null,
): Record<string, unknown> | null {
  if (service.consumer?.kind !== "d601-k3s-managed") return fallbackTarget;
  const target = service.consumer.target;
  const liveBlockReason = environment === "prod" ? prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? null : null;
  const dryRunBlockedReason = artifactConsumerDryRunBlockedServiceIds.get(service.id) ?? null;
  return {
    consumerKind: service.consumer.kind,
    runtimeHost: "D601",
    deploymentMode: fallbackService.deployment.mode,
    noRuntimeSourceBuild: service.consumer.noRuntimeSourceBuild,
    dryRunOnly: dryRunBlockedReason !== null || (environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)),
    blockedReason: dryRunBlockedReason ?? liveBlockReason,
    namespace: target.namespace,
    deployment: target.deployment,
    service: target.service,
    containerName: target.containerName,
    manifest: target.manifestRepoPath,
    targetImage: target.stableImage,
    deployCommandShape: "registry manifest check + native k3s containerd import + Kubernetes Deployment image/env/annotation update + API service proxy health",
    forbiddenActions: ["docker build", "docker compose build", "NodePort", "hostPort", "provider-gateway direct business backend"],
    sourceOfTruth: deployJsonSourceOfTruth(service, environment),
  };
}

export function artifactConsumerTargetImageHint(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string {
  if (service.id === "frontend" && environment === "prod") return "unidesk-frontend";
  if (service.id === "frontend" && environment === "dev") return "unidesk-frontend:dev";
  if (service.id === "k3sctl-adapter") return "unidesk-k3sctl-adapter:d601";
  if (service.id === "met-nonlinear") return "met-nonlinear-ml:tf26";
  if (service.deployment.mode === "k3sctl-managed") return `unidesk-${service.id}:${environment === "dev" ? "dev" : "d601"}`;
  const container = service.repository.containerName;
  if (container.endsWith("-backend")) return container.slice(0, -"-backend".length);
  return container;
}

export function deployJsonExecutorMirrors(
  service: DeployManifestService,
  serviceConfig: UniDeskMicroserviceConfig,
  environment: DeployEnvironment,
  planKind: ArtifactConsumerPlanKind,
  planTarget: Record<string, unknown> | null,
): DeployJsonExecutorMirror[] {
  const mirrors: DeployJsonExecutorMirror[] = [];
  mirrors.push({
    surface: "deploy-executor-plan",
    artifact: {
      kind: "source-build",
      repository: `unidesk/${service.id}`,
      tag: "commitId",
    },
    consumer: {
      kind: planKind === "d601-k3s-managed" ? "d601-k3s-managed" : planKind,
      noRuntimeSourceBuild: planKind !== "d601-dev-target-side-build",
      target: planTarget === null ? undefined : {
        namespace: typeof planTarget.namespace === "string" ? planTarget.namespace : undefined,
        deployment: typeof planTarget.deployment === "string" ? planTarget.deployment : undefined,
        service: typeof planTarget.service === "string" ? planTarget.service : undefined,
        containerName: typeof planTarget.containerName === "string" ? planTarget.containerName : undefined,
        stableImage: typeof planTarget.targetImage === "string" ? planTarget.targetImage : undefined,
        manifestRepoPath: typeof planTarget.manifest === "string" ? planTarget.manifest : undefined,
      },
    },
    runtime: {
      containerPort: serviceConfig.backend.nodePort,
      servicePort: serviceConfig.backend.nodePort,
      healthPath: serviceConfig.backend.healthPath,
      health: service.runtime?.health === undefined ? undefined : {
        deployMetadataRequired: true,
      },
    },
  });
  const manifestMirror = k3sManifestExecutorMirror(service);
  if (manifestMirror !== null) mirrors.push(manifestMirror);
  return mirrors;
}

export function artifactConsumerPlanValidation(service: UniDeskMicroserviceConfig, environment: DeployEnvironment): string[] {
  const kind = artifactConsumerPlanKind(service, environment);
  if (kind === "d601-dev-target-side-build") {
    return [
      "reads origin/master:deploy.json for dev desired state",
      "materializes and builds only the selected dev service on D601",
      "rolls out only unidesk-dev resources",
    ];
  }
  const base = [
    "reads origin/master:deploy.json for commit intent",
    "requires a commit-pinned artifact in 127.0.0.1:5000",
    "verifies image labels for service id, source repo, source commit, and Dockerfile",
    "does not build source on the runtime target",
  ];
  if (kind === "d601-k3s-managed") {
    return [
      ...base,
      "imports the artifact into native k3s containerd",
      "verifies Deployment metadata and health through the Kubernetes API service proxy",
    ];
  }
  if (kind === "d601-direct-compose" || kind === "main-server-compose") {
    return [
      ...base,
      "recreates only the selected Compose service with --no-build --no-deps --force-recreate",
      "verifies running image labels and private service health",
      ...(service.id === "todo-note" ? [
        "todo-note runtime proof synthesizes health.deploy.commit/requestedCommit from Compose container env, container labels, and image labels; it does not infer commit from /root/todo_note or any source directory",
      ] : []),
    ];
  }
  return ["unsupported service remains blocked before source materialization or runtime mutation"];
}

export function unsupportedEnvironmentPlanReason(serviceId: string, environment: DeployEnvironment): string {
  if (environment === "prod" && serviceId === "code-queue") {
    return "Code Queue production CD is intentionally unsupported in this phase: only CI image publication and dev artifact-consumer validation are implemented; prod artifact deploy, rollout and manifest mutation are blocked.";
  }
  return environment === "prod"
    ? "No standardized prod D601 registry artifact consumer is implemented for this service; legacy maintenance-channel deployment is not allowed."
    : "No standardized dev deployment or artifact validation path is implemented for this service.";
}

export function unsupportedPlanTarget(serviceId: string, environment: DeployEnvironment, reason: string): Record<string, unknown> {
  const forbiddenActions = [
    "docker build",
    "docker compose build",
    "docker compose up --build",
    "maintenance-channel source deploy",
    "dirty worktree source deploy",
  ];
  if (serviceId === "code-queue") {
    forbiddenActions.push(
      "codex deploy",
      "server rebuild code-queue",
      "production namespace mutation",
      "production manifest mutation",
      "interrupt running Code Queue tasks",
      "cancel running Code Queue tasks",
    );
  }
  return {
    consumerKind: "unsupported",
    runtimeHost: null,
    deploymentMode: "none",
    noRuntimeSourceBuild: true,
    dryRunOnly: true,
    blockedReason: reason,
    deployCommandShape: "none",
    forbiddenActions,
    ...(serviceId === "code-queue"
      ? {
        excludedTargets: [
          {
            namespace: "unidesk",
            deployments: ["code-queue", "code-queue-read", "code-queue-write", "d601-provider-egress-proxy", "d601-tcp-egress-gateway"],
            reason: `${environment} dry-run must not mutate production Code Queue execution-plane objects.`,
          },
          {
            operations: ["interrupt", "cancel", "restart scheduler", "restart runner"],
            reason: "Code Queue cannot self-deploy or alter active task control state from this runner path.",
          },
        ],
      }
      : {}),
  };
}

export function codeQueueSelfBootstrapGuard(environment: DeployEnvironment): Record<string, unknown> | undefined {
  if (environment !== "dev" && environment !== "prod") return undefined;
  return {
    check: "code-queue-self-bootstrap-guard",
    selfBootstrapBlocked: true,
    requiresSupervisorApproval: true,
    actorBoundary: "a running Code Queue task may publish contract evidence and dry-run plans only; it must not deploy Code Queue itself",
    devApply: "requires explicit supervisor or human authorization outside the running Code Queue task",
    prodApply: "unsupported until a separate supervisor-approved production Code Queue CD design exists",
    allowedWithoutApproval: [
      "CI artifact publication",
      "deploy plan",
      "deploy apply --dry-run",
      "artifact-registry deploy-service --dry-run",
    ],
    forbiddenActions: [
      "self-deploy Code Queue from Code Queue",
      "run a non-dry-run DEV apply from Code Queue",
      "production namespace mutation",
      "production manifest mutation",
      "scheduler or runner restart",
      "active task interrupt",
      "active task cancel",
    ],
    environment,
  };
}

export function codeQueueCiCdBoundary(serviceId: string, environment: DeployEnvironment): Record<string, unknown> | undefined {
  if (serviceId !== "code-queue") return undefined;
  return {
    serviceId,
    selfDeployBlocked: true,
    requiresSupervisorApproval: true,
    selfBootstrapGuard: codeQueueSelfBootstrapGuard(environment),
    ciProducer: {
      command: "bun scripts/cli.ts ci publish-user-service --service code-queue --commit <full-sha>",
      allowed: true,
      outputImage: "127.0.0.1:5000/unidesk/code-queue:<full-sha>",
      responsibility: "build a commit-pinned Code Queue image from pushed Git source, push it to the D601 registry, and report digest/label evidence",
      forbiddenActions: ["deploy apply", "kubectl apply", "rollout restart", "interrupt", "cancel"],
    },
    cdConsumer: environment === "dev"
      ? {
        environment: "dev",
        dryRunCommand: "bun scripts/cli.ts deploy apply --env dev --service code-queue --commit <full-sha> --dry-run",
        liveApplyCommandShape: null,
        liveApplyAllowed: false,
        requiresSupervisorApproval: true,
        allowedTarget: "unidesk-dev Code Queue scheduler/read/write/provider-egress-proxy only",
        manualAuthorizationPoint: "operator reviews CI artifact summary and dev dry-run plan, then explicitly authorizes DEV apply outside this Code Queue task",
        prodMutationAllowed: false,
      }
      : {
        environment: "prod",
        dryRunCommand: "bun scripts/cli.ts deploy plan --env prod --service code-queue",
        liveApplyCommandShape: null,
        liveApplyAllowed: false,
        requiresSupervisorApproval: true,
        allowedTarget: null,
        manualAuthorizationPoint: "PROD requires a future supervisor-approved Code Queue CD design; current runner output is plan/unsupported only",
        prodMutationAllowed: false,
      },
    manualAuthorization: {
      dev: "DEV live apply is a human operator decision after dry-run evidence; this task may not execute it.",
      prod: "PROD live apply is not implemented and must remain unsupported, even with manual intent, until a separate reviewed CD consumer exists.",
    },
    forbiddenActions: [
      "codex deploy",
      "server rebuild code-queue",
      "deploy apply --env prod --service code-queue",
      "artifact-registry deploy-service --env prod --service code-queue",
      "kubectl apply to namespace unidesk",
      "rollout restart production Code Queue scheduler/read/write",
      "interrupt or cancel active Code Queue tasks",
    ],
  };
}
