// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. plan module for scripts/src/hwlab-node-impl.ts.

// Moved mechanically from scripts/src/hwlab-node-impl.ts:440-567 for #903.

// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-25-p0-web-probe-sentinel.
// Responsibility: YAML-first node/lane operations, including Workbench observability control commands.
import { createHash, randomBytes } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { repoRoot, rootPath, type Config } from "../config";
import { runCommand, type CommandResult } from "../command";
import { startJob } from "../jobs";
import { classifySshTcpPoolFailure } from "../ssh";
import { HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH, hwlabNodeControlPlaneInfraHelp, runHwlabNodeControlPlaneInfra } from "../hwlab-node-control-plane";
import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes";
import { nodeWebProbeScriptRunnerSource } from "../hwlab-node-web-probe-runner-source";
import { nodeWebObserveAnalyzerSource } from "../hwlab-node-web-observe-analyzer-source";
import { nodeWebObserveRunnerSource } from "../hwlab-node-web-observe-runner-source";
import { nodeWebObserveCollectViewNodeScript, parseNodeWebProbeObserveCollectView, type NodeWebProbeObserveCollectView } from "../hwlab-node-web-observe-collect";
import { withWebObserveCollectRendered, withWebObserveCommandRendered, withWebObserveStatusRendered } from "../hwlab-node-web-observe-render";
import { buildWebObserveWrapperForObserveOptions, webObserveWrapperStateDirFromStatus } from "../hwlab-node-web-observe-wrapper";
import { renderWebObserveWrapperContract } from "../hwlab-node-web-observe-wrapper-render";
import { runWebProbeSentinelCommand, type WebProbeSentinelOptions } from "../hwlab-node-web-sentinel-cicd";
import { hwlabNodeHelp, hwlabNodeObservabilityHelp, hwlabNodeWebProbeHelp } from "../hwlab-node-help";
import { compactWebProbeResult, compactWebProbeScriptResult } from "../hwlab-node-web-probe-summary";
import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRuleSummaries, nodeObservabilityWarningAlertExpression, nodeObservabilityWarningAlertSummaries } from "../hwlab-node-observability-promql";
import { runDelegatedHwlabNodeCommand, type DelegatedNodeDomain } from "../hwlab-node-transport";
import type { RenderedCliResult } from "../output";

import { NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS } from "./entry";
import { publicExposureSummary } from "./public-exposure";
import { assertNodeId, positiveIntegerOption, requiredOption } from "./utils";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";

export function parseNodeScopedDelegatedOptions(domain: DelegatedNodeDomain, args: string[]): {
  domain: DelegatedNodeDomain;
  action: string;
  runtimeImageAction: string | null;
  node: string;
  lane: HwlabRuntimeLane;
  confirm: boolean;
  dryRun: boolean;
  wait: boolean;
  rerun: boolean;
  allowLiveDbRead: boolean;
  timeoutSeconds: number;
  originalArgs: string[];
  spec: HwlabRuntimeLaneSpec;
} {
  const [actionRaw] = args;
  if (typeof actionRaw !== "string" || actionRaw.startsWith("--")) throw new Error(`${domain} usage: ${domain} ACTION --node NODE --lane vNN [--dry-run|--confirm]`);
  const runtimeImageAction = actionRaw === "runtime-image" && typeof args[1] === "string" && !args[1].startsWith("--") ? args[1] : null;
  const node = requiredOption(args, "--node");
  assertNodeId(node);
  const laneRaw = requiredOption(args, "--lane");
  if (!isHwlabRuntimeLane(laneRaw)) throw new Error(`--lane must be one of v02, v03; got ${laneRaw}`);
  const spec = hwlabRuntimeLaneSpecForNode(laneRaw, node);
  const confirm = args.includes("--confirm");
  const dryRun = args.includes("--dry-run");
  if (confirm && dryRun) throw new Error(`${domain} accepts only one of --confirm or --dry-run`);
  return {
    domain,
    action: actionRaw,
    runtimeImageAction,
    node,
    lane: laneRaw,
    confirm,
    dryRun,
    wait: args.includes("--wait"),
    rerun: args.includes("--rerun"),
    allowLiveDbRead: args.includes("--allow-live-db-read"),
    timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS, 3600),
    originalArgs: [...args],
    spec,
  };
}

export function nodeRuntimeLocalPostgresExpectedAbsent(spec: HwlabRuntimeLaneSpec): boolean {
  return spec.runtimeStore?.postgres?.mode === "platform-service";
}

export function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
  const activeExternalPostgres = hwlabRuntimeActiveExternalPostgres(spec);
  return {
    configPath: hwlabRuntimeLaneConfigPath(),
    node: spec.nodeId,
    nodeRoute: spec.nodeRoute,
    nodeKubeRoute: spec.nodeKubeRoute,
    lane: spec.lane,
    sourceBranch: spec.sourceBranch,
    workspace: spec.workspace,
    cicdRepo: spec.cicdRepo,
    git: {
      sourceUrl: spec.gitUrl,
      readUrl: spec.gitReadUrl,
      writeUrl: spec.gitWriteUrl,
    },
    argo: {
      repoURL: spec.argoRepoUrl,
    },
    gitopsBranch: spec.gitopsBranch,
    catalogPath: spec.catalogPath,
    runtimePath: spec.runtimePath,
    runtimeNamespace: spec.runtimeNamespace,
    renderDir: spec.runtimeRenderDir,
    pipeline: spec.pipeline,
    pipelineRunPrefix: spec.pipelineRunPrefix,
    serviceAccount: spec.serviceAccountName,
    argoApplication: spec.app,
    registryPrefix: spec.registryPrefix,
    baseImage: {
      image: spec.baseImage,
      sourceImage: spec.baseImageSource ?? null,
    },
    serviceIds: spec.serviceIds,
    buildkit: spec.buildkit === undefined ? null : {
      sidecarImage: spec.buildkit.sidecarImage,
      sourceImage: spec.buildkit.sourceImage,
    },
    dockerBuildProxy: {
      http: spec.networkProfile.dockerBuildProxy.http,
      https: spec.networkProfile.dockerBuildProxy.https,
      all: spec.networkProfile.dockerBuildProxy.all,
      noProxy: spec.networkProfile.dockerBuildProxy.noProxy,
    },
    stepEnv: spec.stepEnv,
    public: {
      webUrl: spec.publicWebUrl,
      apiUrl: spec.publicApiUrl,
    },
    webProbe: spec.webProbe === undefined ? null : {
      browserProxyMode: spec.webProbe.browserProxyMode ?? null,
      defaultOrigin: spec.webProbe.defaultOrigin ?? null,
    },
    bootstrapAdmin: spec.bootstrapAdmin === undefined ? null : {
      username: spec.bootstrapAdmin.username,
      displayName: spec.bootstrapAdmin.displayName,
      passwordSourceRef: spec.bootstrapAdmin.passwordSourceRef,
      passwordSourceKey: spec.bootstrapAdmin.passwordSourceKey,
      passwordHashTransform: spec.bootstrapAdmin.passwordHashTransform,
      secretName: spec.bootstrapAdmin.secretName,
      secretKey: spec.bootstrapAdmin.secretKey,
      rolloutDeployment: spec.bootstrapAdmin.rolloutDeployment,
      valuesPrinted: false,
    },
    codeAgentRuntime: spec.codeAgentRuntime === undefined ? null : {
      enabled: spec.codeAgentRuntime.enabled,
      adapter: spec.codeAgentRuntime.adapter,
      managerUrl: spec.codeAgentRuntime.managerUrl,
      apiKeySecretName: spec.codeAgentRuntime.apiKeySecretName,
      apiKeySecretKey: spec.codeAgentRuntime.apiKeySecretKey,
      runnerNamespace: spec.codeAgentRuntime.runnerNamespace,
      secretNamespace: spec.codeAgentRuntime.secretNamespace,
      repoUrlFrom: spec.codeAgentRuntime.repoUrlFrom,
      repoUrl: spec.gitReadUrl,
      providerIdFrom: spec.codeAgentRuntime.providerIdFrom,
      providerId: spec.nodeId,
      defaultProviderProfile: spec.codeAgentRuntime.defaultProviderProfile,
      codexStdioSupervisor: spec.codeAgentRuntime.codexStdioSupervisor,
      valuesPrinted: false,
    },
    sourceWorkspace: spec.sourceWorkspace === undefined ? null : {
      requiredCommands: spec.sourceWorkspace.requiredCommands,
      requiredFiles: spec.sourceWorkspace.requiredFiles,
      install: spec.sourceWorkspace.install,
    },
    publicExposure: spec.publicExposure === null ? null : publicExposureSummary(spec.publicExposure),
    runtimeStore: spec.runtimeStore ?? null,
    downloadProfile: {
      id: spec.downloadProfileId,
      git: spec.downloadProfile.git,
      npm: spec.downloadProfile.npm,
    },
    observability: spec.observability,
    runtimeImageRewrites: spec.runtimeImageRewrites,
    runtimeImageBuilds: spec.runtimeImageBuilds,
    externalPostgres: spec.externalPostgres === undefined ? null : {
      active: activeExternalPostgres !== undefined,
      provider: spec.externalPostgres.provider,
      configRef: spec.externalPostgres.configRef,
      serviceName: spec.externalPostgres.serviceName,
      endpointAddress: spec.externalPostgres.endpointAddress,
      port: spec.externalPostgres.port,
      runtimeAccess: spec.externalPostgres.runtimeAccess ?? null,
      sslmode: spec.externalPostgres.sslmode,
      database: spec.externalPostgres.database,
      cloudApi: {
        secretName: spec.externalPostgres.cloudApi.secretName,
        secretKey: spec.externalPostgres.cloudApi.secretKey,
        sourceRef: spec.externalPostgres.cloudApi.sourceRef,
        envKey: spec.externalPostgres.cloudApi.envKey,
        role: spec.externalPostgres.cloudApi.role,
      },
      openfga: {
        secretName: spec.externalPostgres.openfga.secretName,
        secretKey: spec.externalPostgres.openfga.secretKey,
        sourceRef: spec.externalPostgres.openfga.sourceRef,
        envKey: spec.externalPostgres.openfga.envKey,
        authnKey: spec.externalPostgres.openfga.authnKey ?? null,
        role: spec.externalPostgres.openfga.role,
        schema: spec.externalPostgres.openfga.schema ?? null,
      },
      valuesPrinted: false,
    },
    localPostgres: {
      shouldRender: spec.runtimeStore?.postgres?.mode !== "platform-service",
      expectedAbsent: spec.runtimeStore?.postgres?.mode === "platform-service",
    },
  };
}
