// SPEC: issue-1616 YAML-first AgentRun provider profile online configuration.
// Provides a thin CLI over YAML-declared provider credential Secrets, runtime GitOps env and bounded validation.
import type { UniDeskConfig } from "../config";
import { startJob } from "../jobs";
import { sha256Fingerprint } from "../platform-infra-ops-library";
import {
  agentRunImageArtifact,
  renderedFilesDigest,
  renderAgentRunGitopsFiles,
} from "../agentrun-manifests";
import {
  agentRunLaneSummary,
  agentRunProviderCredentialRefs,
  resolveAgentRunLaneTarget,
  type AgentRunLaneSecretSpec,
  type AgentRunLaneSpec,
} from "../agentrun-lanes";
import { refreshYamlLaneScript } from "./git-mirror";
import type { ConfirmOptions, DisclosureOptions } from "./options";
import { compactAgentRunLaneStatusTarget } from "./trigger";
import {
  collectLaneSecretSources,
  createYamlLaneJobScript,
  inspectSecretSourceValue,
  restartYamlLaneScript,
  secretSyncScript,
  type LaneSecretSource,
  type LaneSecretSourceInspection,
  yamlLaneGitopsPublishJobManifest,
  yamlLaneGitopsPublishPayloadFromProbe,
  yamlLaneJobProbeScript,
} from "./secrets";
import { capture, captureJsonPayload, compactCapture, progressEvent, record, shQuote, sleep, stringOrNull } from "./utils";

export interface ProviderProfileOptions extends ConfirmOptions, DisclosureOptions {
  readonly node: string | null;
  readonly lane: string | null;
  readonly profile: string | null;
  readonly wait: boolean;
  readonly prompt: string | null;
}

export function parseProviderProfileOptions(args: string[]): ProviderProfileOptions {
  let node: string | null = null;
  let lane: string | null = null;
  let profile: string | null = null;
  let full = false;
  let raw = false;
  let wait = false;
  let prompt: string | null = null;
  let confirm = false;
  let dryRun = false;
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index];
    if (arg === "--confirm") {
      confirm = true;
      continue;
    }
    if (arg === "--dry-run") {
      dryRun = true;
      continue;
    }
    if (arg === "--full") {
      full = true;
      continue;
    }
    if (arg === "--raw") {
      raw = true;
      continue;
    }
    if (arg === "--wait") {
      wait = true;
      continue;
    }
    if (arg === "--node" || arg === "--lane" || arg === "--profile" || arg === "--prompt") {
      const value = args[index + 1];
      if (value === undefined || value.startsWith("--")) throw new Error(`${arg} requires a value`);
      if (arg === "--node") node = value;
      if (arg === "--lane") lane = value;
      if (arg === "--profile") profile = value;
      if (arg === "--prompt") prompt = value;
      index += 1;
      continue;
    }
    throw new Error(`unsupported provider-profile option: ${arg}`);
  }
  if (confirm && dryRun) throw new Error("accepts only one of --confirm or --dry-run");
  return { node, lane, profile, full, raw, wait, prompt, confirm, dryRun: dryRun || !confirm };
}

export function providerProfileHelp(): string {
  return [
    "Usage: bun scripts/cli.ts agentrun provider-profile <plan|status|runtime-status|apply|validate> --node <NODE> --lane <lane> --profile <profile> [options]",
    "",
    "Examples:",
    "  bun scripts/cli.ts agentrun provider-profile plan --node NC01 --lane nc01-v02 --profile gpt.pika",
    "  bun scripts/cli.ts agentrun provider-profile status --node NC01 --lane nc01-v02 --profile gpt.pika",
    "  bun scripts/cli.ts agentrun provider-profile runtime-status --node NC01 --lane nc01-v02 --profile gpt.pika",
    "  bun scripts/cli.ts agentrun provider-profile apply --node NC01 --lane nc01-v02 --profile gpt.pika --confirm",
    "  bun scripts/cli.ts job status <jobId> --tail-bytes 12000",
    "  bun scripts/cli.ts agentrun provider-profile validate --node NC01 --lane nc01-v02 --profile gpt.pika --wait",
    "",
    "Boundary:",
    "  confirmed apply syncs the selected YAML-declared provider Secret, publishes runtime GitOps using the current manager image, refreshes Argo and restarts the manager without rebuilding images or creating a PipelineRun.",
  ].join("\n");
}

export async function runProviderProfileCommand(config: UniDeskConfig, action: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  const options = parseProviderProfileOptions(args);
  if (action === "plan") return providerProfilePlan(options);
  if (action === "status") return providerProfileStatus(config, options);
  if (action === "runtime-status") return providerProfileRuntimeStatus(config, options);
  if (action === "apply") {
    if (options.confirm && !options.wait) return providerProfileApplyAsync(config, options);
    return providerProfileApply(config, options);
  }
  if (action === "validate") return providerProfileValidate(config, options);
  throw new Error(`unsupported provider-profile action: ${action ?? "(empty)"}`);
}

async function providerProfileApplyAsync(config: UniDeskConfig, options: ProviderProfileOptions): Promise<Record<string, unknown>> {
  const plan = providerProfilePlan({ ...options, confirm: false, dryRun: true });
  if (plan.ok !== true) return plan;
  const { spec, profile } = resolveProviderProfile(options);
  const secrets = spec.secrets.filter((secret) => secret.providerCredentialProfile === profile);
  const args = [
    "bun",
    "scripts/cli.ts",
    "agentrun",
    "provider-profile",
    "apply",
    "--node",
    spec.nodeId,
    "--lane",
    spec.lane,
    "--profile",
    profile,
    "--confirm",
    "--wait",
  ];
  const jobName = `agentrun_${spec.lane}_provider_profile_${profile.replace(/[^A-Za-z0-9_-]/gu, "-")}_apply`;
  const job = startJob(jobName, args, `Apply YAML-declared AgentRun provider profile ${profile} on ${spec.nodeId}/${spec.lane}`);
  const statusCommand = `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`;
  return {
    ok: true,
    command: "agentrun provider-profile apply",
    mode: "async-job",
    mutation: true,
    node: spec.nodeId,
    lane: spec.lane,
    profile,
    job,
    jobCreated: true,
    statusCommand,
    next: {
      status: statusCommand,
      full: `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000 --full`,
    },
    valuesPrinted: false,
  };
}

async function providerProfileRuntimeStatus(config: UniDeskConfig, options: ProviderProfileOptions): Promise<Record<string, unknown>> {
  const { configPath, spec, profile, secrets } = resolveProviderProfile(options);
  const live = await capture(config, spec.nodeKubeRoute, ["sh", "--", providerProfileRuntimeStatusScript(spec, secrets)]);
  const payload = captureJsonPayload(live);
  const ready = live.exitCode === 0
    && payload.ok === true
    && record(payload.secrets).ready === true
    && record(payload.providerServices).ready === true;
  return {
    ok: ready,
    command: "agentrun provider-profile runtime-status",
    mode: "runtime-only-profile-preflight",
    status: ready ? "ready" : "blocked",
    degradedReason: ready ? null : "runtime-provider-profile-unavailable",
    mutation: false,
    ownerSourceInspected: false,
    runtimeSecretValuesDecoded: false,
    configPath,
    target: options.full ? agentRunLaneSummary(spec) : compactAgentRunLaneStatusTarget(spec),
    profile: providerProfileSummary(spec, profile, secrets),
    runtime: payload,
    capture: compactCapture(live, { full: options.full || options.raw || live.exitCode !== 0, stdoutTailChars: 2000, stderrTailChars: 2000 }),
    valuesPrinted: false,
  };
}

function resolveProviderProfile(options: ProviderProfileOptions): { configPath: string; spec: AgentRunLaneSpec; profile: string; secrets: AgentRunLaneSecretSpec[] } {
  if (options.profile === null) throw new Error("provider-profile requires --profile");
  const target = resolveAgentRunLaneTarget(options);
  const secrets = target.spec.secrets.filter((secret) => secret.providerCredentialProfile === options.profile);
  if (secrets.length === 0) {
    const declared = agentRunProviderCredentialRefs(target.spec).map((item) => item.profile);
    throw new Error(`provider profile ${options.profile} is not declared in ${target.configPath}; declared profiles: ${declared.join(", ") || "(none)"}`);
  }
  return { ...target, profile: options.profile, secrets };
}

function providerProfilePlan(options: ProviderProfileOptions): Record<string, unknown> {
  const { configPath, spec, profile, secrets } = resolveProviderProfile(options);
  const inspected = inspectProviderProfileSources(spec, secrets);
  const source = providerProfileSourceSummary(inspected);
  const discloseFull = options.full || options.raw;
  const values = inspected.map(({ secret, inspection }) => {
    return {
      id: secret.id,
      sourceRef: secret.sourceRef,
      sourceMode: secret.sourceMode,
      transform: secret.transform,
      targetRef: secret.targetRef,
      key: secret.targetRef.key,
      codexConfig: secret.codexConfig === null ? null : codexConfigSummary(secret.codexConfig),
      ...inspection.summary,
      valuesPrinted: false,
    };
  });
  return {
    ok: source.ready,
    command: "agentrun provider-profile plan",
    mode: "yaml-first-online-profile",
    mutation: false,
    status: source.ready ? "ready" : "blocked",
    degradedReason: source.ready ? null : "lane-secret-source-unavailable",
    blockedBeforeMutation: !source.ready,
    configPath,
    target: discloseFull ? agentRunLaneSummary(spec) : compactAgentRunLaneStatusTarget(spec),
    profile: providerProfileSummary(spec, profile, secrets),
    source: discloseFull ? source : compactProviderProfileSource(source),
    plan: {
      secretCount: values.length,
      secrets: discloseFull ? values : compactProviderProfilePlanSecrets(values),
      runtime: {
        managerDeployment: spec.runtime.managerDeployment,
        runnerEgressProxyUrl: spec.deployment.runner.egressProxyUrl,
        runnerNoProxyExtra: spec.deployment.runner.noProxyExtra,
        providerServices: providerRuntimeServiceTargets(secrets),
        valuesPrinted: false,
      },
      applySteps: ["secret-sync", "gitops-publish-current-image", "argocd-refresh-sync", "manager-rollout-restart"],
      rebuildImages: false,
      pipelineRun: false,
      valuesPrinted: false,
    },
    next: {
      apply: `bun scripts/cli.ts agentrun provider-profile apply --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --confirm`,
      status: `bun scripts/cli.ts agentrun provider-profile status --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile}`,
    },
    valuesPrinted: false,
  };
}

function compactProviderProfilePlanSecrets(values: readonly Record<string, unknown>[]): Record<string, unknown>[] {
  return values.map((item) => ({
    id: item.id ?? null,
    targetRef: item.targetRef ?? null,
    key: item.key ?? null,
    present: item.present === true,
    fingerprint: item.fingerprint ?? null,
    valueBytes: item.valueBytes ?? null,
    degradedReason: item.degradedReason ?? null,
    valuesPrinted: false,
  }));
}

async function providerProfileStatus(config: UniDeskConfig, options: ProviderProfileOptions): Promise<Record<string, unknown>> {
  const { configPath, spec, profile, secrets } = resolveProviderProfile(options);
  const inspected = inspectProviderProfileSources(spec, secrets);
  const source = providerProfileSourceSummary(inspected);
  if (!source.ready) return providerProfileSourceUnavailable("status", configPath, spec, profile, secrets, source);
  const live = await capture(config, spec.nodeKubeRoute, ["sh", "--", providerProfileLiveStatusScript(spec, secrets)]);
  const payload = captureJsonPayload(live);
  const comparison = providerProfileLiveComparison(inspected, payload);
  const discloseFull = options.full || options.raw;
  const yamlDesired = {
    secrets: inspected.map(({ secret, inspection }) => ({
      id: secret.id,
      targetRef: secret.targetRef,
      key: secret.targetRef.key,
      fingerprint: inspection.summary.fingerprint,
      valueBytes: inspection.summary.valueBytes,
      codexConfig: secret.codexConfig === null ? null : codexConfigSummary(secret.codexConfig),
      valuesPrinted: false,
    })),
    runnerNoProxyExtra: spec.deployment.runner.noProxyExtra,
    runnerEgressProxyUrl: spec.deployment.runner.egressProxyUrl,
    valuesPrinted: false,
  };
  return {
    ok: live.exitCode === 0 && payload.ok !== false && payload.runtimeSecretValuesDecoded === false && comparison.ready,
    command: "agentrun provider-profile status",
    mode: "yaml-first-online-profile",
    mutation: false,
    configPath,
    target: options.full ? agentRunLaneSummary(spec) : compactAgentRunLaneStatusTarget(spec),
    profile: providerProfileSummary(spec, profile, secrets),
    source: discloseFull ? source : compactProviderProfileSource(source),
    yamlDesired: discloseFull ? yamlDesired : compactProviderProfileDesired(yamlDesired),
    live: discloseFull
      ? { ...payload, desiredComparison: comparison, valuesPrinted: false }
      : compactProviderProfileLive(payload, comparison),
    capture: compactCapture(live, { full: options.full || options.raw || live.exitCode !== 0, stdoutTailChars: 3000, stderrTailChars: 3000 }),
    next: {
      apply: `bun scripts/cli.ts agentrun provider-profile apply --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --confirm`,
      validate: `bun scripts/cli.ts agentrun provider-profile validate --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --wait`,
    },
    valuesPrinted: false,
  };
}

function compactProviderProfileSource(source: ReturnType<typeof providerProfileSourceSummary>): Record<string, unknown> {
  return {
    ready: source.ready,
    count: source.count,
    presentCount: source.presentCount,
    missingCount: source.missingCount,
    missing: source.items
      .filter((item) => item.present !== true)
      .map((item) => ({ id: item.id ?? null, targetRef: item.targetRef ?? null, degradedReason: item.degradedReason ?? null, valuesPrinted: false })),
    valuesPrinted: false,
  };
}

function compactProviderProfileDesired(desired: Record<string, unknown>): Record<string, unknown> {
  const secrets = Array.isArray(desired.secrets) ? desired.secrets.map(record) : [];
  return {
    secretCount: secrets.length,
    secrets: secrets.map((item) => ({
      id: item.id ?? null,
      targetRef: item.targetRef ?? null,
      fingerprint: item.fingerprint ?? null,
      valueBytes: item.valueBytes ?? null,
      valuesPrinted: false,
    })),
    runnerEgressProxyUrl: desired.runnerEgressProxyUrl ?? null,
    runnerNoProxyExtraCount: Array.isArray(desired.runnerNoProxyExtra) ? desired.runnerNoProxyExtra.length : 0,
    valuesPrinted: false,
  };
}

function compactProviderProfileLive(payload: Record<string, unknown>, comparison: ReturnType<typeof providerProfileLiveComparison>): Record<string, unknown> {
  const manager = record(payload.manager);
  const secrets = record(payload.secrets);
  return {
    ok: payload.ok === true,
    manager: {
      deploymentExists: manager.deploymentExists === true,
      readyReplicas: manager.readyReplicas ?? null,
      replicas: manager.replicas ?? null,
      sourceCommit: manager.sourceCommit ?? null,
      envIdentity: manager.envIdentity ?? null,
      runnerEgressProxyUrl: manager.runnerEgressProxyUrl ?? null,
      noProxyContainsHyue: manager.noProxyContainsHyue === true,
      noProxyContainsPikapython: manager.noProxyContainsPikapython === true,
      valuesPrinted: false,
    },
    secrets: {
      count: secrets.count ?? null,
      ready: secrets.ready === true,
      items: Array.isArray(secrets.items) ? secrets.items.map(record).map((item) => ({
        namespace: item.namespace ?? null,
        name: item.name ?? null,
        key: item.key ?? null,
        present: item.present === true,
        keyPresent: item.keyPresent === true,
        encodedBytes: item.encodedBytes ?? null,
        encodedFingerprint: item.encodedFingerprint ?? null,
        valuesPrinted: false,
      })) : [],
      valuesPrinted: false,
    },
    providerServices: {
      count: record(payload.providerServices).count ?? null,
      ready: record(payload.providerServices).ready === true,
      items: Array.isArray(record(payload.providerServices).items)
        ? (record(payload.providerServices).items as unknown[]).map(record).map((item) => ({
          providerId: item.providerId ?? null,
          baseUrl: item.baseUrl ?? null,
          namespace: item.namespace ?? null,
          name: item.name ?? null,
          port: item.port ?? null,
          present: item.present === true,
          portPresent: item.portPresent === true,
          valuesPrinted: false,
        }))
        : [],
      valuesPrinted: false,
    },
    desiredComparison: comparison,
    runtimeSecretValuesDecoded: payload.runtimeSecretValuesDecoded === true,
    valuesPrinted: false,
  };
}

async function providerProfileApply(config: UniDeskConfig, options: ProviderProfileOptions): Promise<Record<string, unknown>> {
  const { configPath, spec, profile, secrets } = resolveProviderProfile(options);
  if (options.dryRun || !options.confirm) return providerProfilePlan({ ...options, dryRun: true, confirm: false });
  const inspected = inspectProviderProfileSources(spec, secrets);
  const source = providerProfileSourceSummary(inspected);
  if (!source.ready) return providerProfileSourceUnavailable("apply", configPath, spec, profile, secrets, source);
  providerProfileApplyProgress(spec, profile, "secret-sync", "running");
  const secretSync = await capture(config, spec.nodeKubeRoute, ["sh", "--", secretSyncScript(spec, inspected.map(({ secret, inspection }) => ({ targetRef: secret.targetRef, value: inspection.sourceValue!.value })))]);
  const secretPayload = captureJsonPayload(secretSync);
  providerProfileApplyProgress(spec, profile, "secret-sync", secretSync.exitCode === 0 && secretPayload.ok !== false ? "succeeded" : "failed", { status: secretPayload.status ?? null });
  if (secretSync.exitCode !== 0 || secretPayload.ok === false) {
    return applyFailure(configPath, spec, profile, "secret-sync", secretPayload, secretSync);
  }
  providerProfileApplyProgress(spec, profile, "current-image-resolve", "running");
  const liveBefore = await capture(config, spec.nodeKubeRoute, ["sh", "--", currentManagerImageScript(spec)]);
  const liveBeforePayload = captureJsonPayload(liveBefore);
  const sourceCommit = stringOrNull(liveBeforePayload.sourceCommit);
  const envIdentity = stringOrNull(liveBeforePayload.envIdentity);
  const digest = stringOrNull(liveBeforePayload.digest);
  if (liveBefore.exitCode !== 0 || sourceCommit === null || envIdentity === null || digest === null) {
    providerProfileApplyProgress(spec, profile, "current-image-resolve", "failed");
    return applyFailure(configPath, spec, profile, "current-image-resolve", liveBeforePayload, liveBefore);
  }
  providerProfileApplyProgress(spec, profile, "current-image-resolve", "succeeded", { sourceCommit });
  const image = agentRunImageArtifact(spec, { sourceCommit, envIdentity, digest, status: "reused" });
  const renderedFiles = renderAgentRunGitopsFiles(spec, { sourceCommit, image });
  providerProfileApplyProgress(spec, profile, "gitops-publish", "running", { sourceCommit });
  const publish = await publishProfileGitops(config, spec, sourceCommit, renderedFiles);
  providerProfileApplyProgress(spec, profile, "gitops-publish", publish.ok ? "succeeded" : "failed", { sourceCommit });
  if (publish.ok !== true) {
    return { ok: false, command: "agentrun provider-profile apply", mode: "confirmed-online-profile", mutation: true, configPath, target: compactAgentRunLaneStatusTarget(spec), profile, phase: "gitops-publish", result: publish, valuesPrinted: false };
  }
  providerProfileApplyProgress(spec, profile, "argocd-refresh-sync", "running", { sourceCommit });
  const argo = await capture(config, spec.nodeKubeRoute, ["sh", "--", providerProfileArgoRefreshSyncScript(spec)]);
  const argoPayload = captureJsonPayload(argo);
  providerProfileApplyProgress(spec, profile, "argocd-refresh-sync", argo.exitCode === 0 && argoPayload.ok !== false ? "succeeded" : "failed", { sourceCommit });
  if (argo.exitCode !== 0 || argoPayload.ok === false) {
    return applyFailure(configPath, spec, profile, "argocd-refresh-sync", argoPayload, argo);
  }
  providerProfileApplyProgress(spec, profile, "manager-restart", "running", { sourceCommit });
  const restart = await capture(config, spec.nodeKubeRoute, ["sh", "--", restartYamlLaneScript(spec)]);
  const restartPayload = captureJsonPayload(restart);
  providerProfileApplyProgress(spec, profile, "manager-restart", restart.exitCode === 0 && restartPayload.ok !== false ? "succeeded" : "failed", { sourceCommit });
  if (restart.exitCode !== 0 || restartPayload.ok === false) {
    return applyFailure(configPath, spec, profile, "manager-restart", restartPayload, restart);
  }
  providerProfileApplyProgress(spec, profile, "runtime-status", "running", { sourceCommit });
  const liveAfter = await capture(config, spec.nodeKubeRoute, ["sh", "--", providerProfileLiveStatusScript(spec, secrets)]);
  const liveAfterPayload = captureJsonPayload(liveAfter);
  providerProfileApplyProgress(spec, profile, "runtime-status", liveAfter.exitCode === 0 && liveAfterPayload.ok !== false ? "succeeded" : "failed", { sourceCommit });
  return {
    ok: liveAfter.exitCode === 0 && liveAfterPayload.ok !== false,
    command: "agentrun provider-profile apply",
    mode: "confirmed-online-profile",
    mutation: true,
    configPath,
    target: compactAgentRunLaneStatusTarget(spec),
    profile: providerProfileSummary(spec, profile, secrets),
    steps: {
      secretSync: secretPayload,
      gitops: publish.payload,
      argo: argoPayload,
      restart: restartPayload,
      live: liveAfterPayload,
      valuesPrinted: false,
    },
    renderedFiles: {
      count: renderedFiles.length,
      digest: renderedFilesDigest(renderedFiles),
      valuesPrinted: false,
    },
    image: { sourceCommit, envIdentity, digest, status: "reused", valuesPrinted: false },
    rebuildImages: false,
    pipelineRun: false,
    captures: options.full || options.raw || liveAfter.exitCode !== 0 ? {
      argo: compactCapture(argo, { full: options.full || options.raw || argo.exitCode !== 0 }),
      restart: compactCapture(restart, { full: options.full || options.raw || restart.exitCode !== 0 }),
      live: compactCapture(liveAfter, { full: options.full || options.raw || liveAfter.exitCode !== 0 }),
    } : undefined,
    next: {
      status: `bun scripts/cli.ts agentrun provider-profile status --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile}`,
      validate: `bun scripts/cli.ts agentrun provider-profile validate --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --wait`,
    },
    valuesPrinted: false,
  };
}

function providerProfileApplyProgress(
  spec: AgentRunLaneSpec,
  profile: string,
  phase: string,
  status: "running" | "succeeded" | "failed",
  detail: Record<string, unknown> = {},
): void {
  progressEvent("agentrun.provider-profile.apply.progress", {
    node: spec.nodeId,
    lane: spec.lane,
    profile,
    phase,
    status,
    ...detail,
    valuesPrinted: false,
  });
}

async function providerProfileValidate(config: UniDeskConfig, options: ProviderProfileOptions): Promise<Record<string, unknown>> {
  const { configPath, spec, profile, secrets } = resolveProviderProfile(options);
  const inspected = inspectProviderProfileSources(spec, secrets);
  const source = providerProfileSourceSummary(inspected);
  if (!source.ready) return providerProfileSourceUnavailable("validate", configPath, spec, profile, secrets, source);
  const prompt = options.prompt ?? `请只回复：${profile} upstream OK`;
  const live = await capture(config, spec.nodeKubeRoute, ["sh", "--", providerProfileLiveStatusScript(spec, secrets)]);
  const livePayload = captureJsonPayload(live);
  const canRun = live.exitCode === 0 && livePayload.ok !== false;
  return {
    ok: canRun,
    command: "agentrun provider-profile validate",
    mode: "profile-runtime-preflight",
    mutation: false,
    configPath,
    target: compactAgentRunLaneStatusTarget(spec),
    profile: providerProfileSummary(spec, profile, secrets),
    source,
    promptBytes: Buffer.byteLength(prompt, "utf8"),
    live: livePayload,
    note: "This command validates YAML/live profile wiring. User-entry Web validation still runs through HWLAB web-probe so it uses the same Workbench path.",
    webProbeNext: `bun scripts/cli.ts web-probe observe command <observer> --type sendPrompt --provider ${profile} --text '${prompt.replaceAll("'", "'\\''")}'`,
    valuesPrinted: false,
  };
}

interface ProviderProfileInspectedSecret {
  readonly secret: AgentRunLaneSecretSpec;
  readonly source: LaneSecretSource | null;
  readonly inspection: LaneSecretSourceInspection;
}

function inspectProviderProfileSources(spec: AgentRunLaneSpec, secrets: readonly AgentRunLaneSecretSpec[]): ProviderProfileInspectedSecret[] {
  const sources = new Map(collectLaneSecretSources(spec).map((source) => [source.id, source]));
  return secrets.map((secret) => {
    const source = sources.get(secret.id) ?? null;
    if (source !== null) return { secret, source, inspection: inspectSecretSourceValue(spec, source) };
    return {
      secret,
      source,
      inspection: {
        ok: false,
        sourceValue: null,
        summary: {
          present: false,
          sourceFilePresent: false,
          sourcePath: secret.sourceRef,
          fingerprint: null,
          valueBytes: null,
          degradedReason: "lane-secret-source-declaration-missing",
          message: `secret ${secret.id} has no resolved lane source`,
          valuesPrinted: false,
        },
      },
    };
  });
}

function providerProfileSourceSummary(items: readonly ProviderProfileInspectedSecret[]): {
  ready: boolean;
  count: number;
  presentCount: number;
  missingCount: number;
  items: Record<string, unknown>[];
  valuesPrinted: false;
} {
  const summaries = items.map(({ secret, inspection }) => ({
    id: secret.id,
    sourceRef: secret.sourceRef,
    sourceMode: secret.sourceMode,
    transform: secret.transform,
    targetRef: secret.targetRef,
    ...inspection.summary,
    valuesPrinted: false,
  }));
  const presentCount = summaries.filter((item) => item.present === true).length;
  return {
    ready: items.length > 0 && items.every((item) => item.inspection.ok),
    count: items.length,
    presentCount,
    missingCount: items.length - presentCount,
    items: summaries,
    valuesPrinted: false,
  };
}

function providerProfileSourceUnavailable(
  action: "status" | "apply" | "validate",
  configPath: string,
  spec: AgentRunLaneSpec,
  profile: string,
  secrets: readonly AgentRunLaneSecretSpec[],
  source: ReturnType<typeof providerProfileSourceSummary>,
): Record<string, unknown> {
  return {
    ok: false,
    command: `agentrun provider-profile ${action}`,
    mode: "profile-source-preflight",
    status: "blocked",
    degradedReason: "lane-secret-source-unavailable",
    mutation: false,
    blockedBeforeMutation: true,
    configPath,
    target: compactAgentRunLaneStatusTarget(spec),
    profile: providerProfileSummary(spec, profile, secrets),
    source,
    next: {
      recheck: `bun scripts/cli.ts agentrun provider-profile status --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --raw`,
      applyAfterOwnerProvision: `bun scripts/cli.ts agentrun provider-profile apply --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --confirm`,
    },
    valuesPrinted: false,
  };
}

function providerProfileLiveComparison(items: readonly ProviderProfileInspectedSecret[], payload: Record<string, unknown>): Record<string, unknown> & { ready: boolean } {
  const liveItems = Array.isArray(record(payload.secrets).items)
    ? (record(payload.secrets).items as unknown[]).map(record)
    : [];
  const byTarget = new Map(liveItems.map((item) => [
    `${stringOrNull(item.namespace) ?? ""}/${stringOrNull(item.name) ?? ""}/${stringOrNull(item.key) ?? ""}`,
    item,
  ]));
  const comparisons = items.map(({ secret, inspection }) => {
    const target = secret.targetRef;
    const live = byTarget.get(`${target.namespace}/${target.name}/${target.key}`) ?? {};
    const desiredEncodedFingerprint = inspection.sourceValue === null
      ? null
      : sha256Fingerprint(Buffer.from(inspection.sourceValue.value, "utf8").toString("base64"));
    const liveEncodedFingerprint = stringOrNull(live.encodedFingerprint);
    return {
      id: secret.id,
      targetRef: target,
      desiredEncodedFingerprint,
      liveEncodedFingerprint,
      present: live.present === true,
      keyPresent: live.keyPresent === true,
      encodedFingerprintMatches: desiredEncodedFingerprint !== null && liveEncodedFingerprint === desiredEncodedFingerprint,
      valuesPrinted: false,
    };
  });
  return {
    ready: comparisons.length > 0 && comparisons.every((item) => item.keyPresent && item.encodedFingerprintMatches),
    count: comparisons.length,
    matchingCount: comparisons.filter((item) => item.encodedFingerprintMatches).length,
    items: comparisons,
    valuesPrinted: false,
  };
}

function applyFailure(configPath: string, spec: AgentRunLaneSpec, profile: string, phase: string, payload: Record<string, unknown>, captureResult: Awaited<ReturnType<typeof capture>>): Record<string, unknown> {
  return {
    ok: false,
    command: "agentrun provider-profile apply",
    mode: "confirmed-online-profile",
    mutation: true,
    configPath,
    target: compactAgentRunLaneStatusTarget(spec),
    profile,
    phase,
    result: payload,
    capture: compactCapture(captureResult, { full: true, stdoutTailChars: 4000, stderrTailChars: 4000 }),
    valuesPrinted: false,
  };
}

async function publishProfileGitops(config: UniDeskConfig, spec: AgentRunLaneSpec, sourceCommit: string, files: readonly { path: string; content: string }[]): Promise<Record<string, unknown> & { ok: boolean; payload: Record<string, unknown> }> {
  const jobName = `profile-gitops-${spec.nodeId.toLowerCase()}-${spec.lane}-${Date.now().toString(36)}`.slice(0, 63);
  const manifest = yamlLaneGitopsPublishJobManifest(spec, files, jobName);
  const created = await capture(config, spec.nodeKubeRoute, ["sh", "--", createYamlLaneJobScript(spec.gitMirror.namespace, jobName, manifest)]);
  if (created.exitCode !== 0) return { ok: false, payload: { ok: false, status: "create-failed", jobName, valuesPrinted: false }, capture: compactCapture(created, { full: true }), valuesPrinted: false };
  const startedAt = Date.now();
  let polls = 0;
  let lastPayload: Record<string, unknown> = {};
  while (Date.now() - startedAt < 300_000) {
    polls += 1;
    const probe = await capture(config, spec.nodeKubeRoute, ["sh", "--", yamlLaneJobProbeScript(spec.gitMirror.namespace, jobName)]);
    const probePayload = captureJsonPayload(probe);
    const publishPayload = yamlLaneGitopsPublishPayloadFromProbe(probePayload);
    if (Object.keys(publishPayload).length > 0) lastPayload = publishPayload;
    progressEvent("agentrun.provider-profile.gitops-publish.progress", { node: spec.nodeId, lane: spec.lane, sourceCommit, jobName, polls, status: stringOrNull(publishPayload.status) ?? "running", valuesPrinted: false });
    if (probePayload.succeeded === true) return { ok: publishPayload.ok === true, payload: publishPayload, jobName, polls, elapsedMs: Date.now() - startedAt, probe: compactCapture(probe), valuesPrinted: false };
    if (probePayload.failed === true) return { ok: false, payload: { ...lastPayload, ok: false, status: "failed", jobName, valuesPrinted: false }, jobName, polls, elapsedMs: Date.now() - startedAt, probe: compactCapture(probe, { full: true }), valuesPrinted: false };
    await sleep(3_000);
  }
  return { ok: false, payload: { ...lastPayload, ok: false, status: "timeout", jobName, valuesPrinted: false }, jobName, polls, elapsedMs: Date.now() - startedAt, valuesPrinted: false };
}

function providerProfileSummary(spec: AgentRunLaneSpec, profile: string, secrets: readonly AgentRunLaneSecretSpec[]): Record<string, unknown> {
  const credential = agentRunProviderCredentialRefs(spec, profile)[0] ?? null;
  return {
    profile,
    declared: true,
    backendProfile: profile === "gpt.pika" ? "gpt-pika" : profile,
    secretIds: secrets.map((secret) => secret.id),
    secretRef: credential?.secretRef ?? null,
    configuredKeys: credential?.secretRef.keys ?? secrets.map((secret) => secret.targetRef.key),
    valuesPrinted: false,
  };
}

function codexConfigSummary(config: NonNullable<AgentRunLaneSecretSpec["codexConfig"]>): Record<string, unknown> {
  return {
    modelProvider: config.modelProvider,
    model: config.model,
    reviewModel: config.reviewModel,
    modelContextWindow: config.modelContextWindow,
    modelAutoCompactTokenLimit: config.modelAutoCompactTokenLimit,
    modelCatalogJson: config.modelCatalogJson,
    approvalsReviewer: config.approvalsReviewer,
    modelProviderCount: config.modelProviders.length,
    providers: config.modelProviders.map((provider) => ({
      id: provider.id,
      baseUrl: provider.baseUrl,
      wireApi: provider.wireApi,
      requiresOpenaiAuth: provider.requiresOpenaiAuth,
    })),
    valuesPrinted: false,
  };
}

function providerRuntimeServiceTargets(secrets: readonly AgentRunLaneSecretSpec[]): Record<string, unknown>[] {
  const targets: Record<string, unknown>[] = [];
  const seen = new Set<string>();
  for (const secret of secrets) {
    for (const provider of secret.codexConfig?.modelProviders ?? []) {
      const parsed = new URL(provider.baseUrl);
      const hostParts = parsed.hostname.split(".");
      if (hostParts.length < 4 || hostParts[2] !== "svc" || hostParts[3] !== "cluster") continue;
      const port = parsed.port.length > 0 ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
      const key = `${provider.id}/${hostParts[1]}/${hostParts[0]}/${port}`;
      if (seen.has(key)) continue;
      seen.add(key);
      targets.push({
        providerId: provider.id,
        baseUrl: provider.baseUrl,
        host: parsed.hostname,
        namespace: hostParts[1],
        name: hostParts[0],
        port,
        valuesPrinted: false,
      });
    }
  }
  return targets;
}

function currentManagerImageScript(spec: AgentRunLaneSpec): string {
  return [
    "set -eu",
    `namespace=${shQuote(spec.runtime.namespace)}`,
    `deployment=${shQuote(spec.runtime.managerDeployment)}`,
    "kubectl -n \"$namespace\" get deployment \"$deployment\" -o json >/tmp/agentrun-provider-profile-current-manager.json",
    "node <<'NODE'",
    "const fs = require('node:fs');",
    " const j = JSON.parse(fs.readFileSync('/tmp/agentrun-provider-profile-current-manager.json', 'utf8'));",
    " const c = (j.spec?.template?.spec?.containers || [])[0] || {};",
    " const env = new Map((c.env || []).map((e) => [e.name, e.value || null]));",
    " const image = c.image || '';",
    " const digest = image.includes('@') ? image.split('@').pop() : null;",
    " console.log(JSON.stringify({ ok: Boolean(env.get('AGENTRUN_SOURCE_COMMIT') && env.get('AGENTRUN_ENV_IDENTITY') && digest), sourceCommit: env.get('AGENTRUN_SOURCE_COMMIT'), envIdentity: env.get('AGENTRUN_ENV_IDENTITY'), digest, imageStatus: 'reused', valuesPrinted: false }));",
    "NODE",
  ].join("\n");
}

function providerProfileLiveStatusScript(spec: AgentRunLaneSpec, secrets: readonly AgentRunLaneSecretSpec[]): string {
  const refs = Buffer.from(JSON.stringify({
    secrets: secrets.map((secret) => secret.targetRef),
    providerServices: providerRuntimeServiceTargets(secrets),
  }), "utf8").toString("base64");
  return [
    "set -eu",
    `namespace=${shQuote(spec.runtime.namespace)}`,
    `deployment=${shQuote(spec.runtime.managerDeployment)}`,
    `refs_b64=${shQuote(refs)}`,
    "kubectl -n \"$namespace\" get deployment \"$deployment\" -o json >/tmp/agentrun-provider-profile-deploy.json",
    "REFS_B64=\"$refs_b64\" node <<'NODE'",
    "const fs = require('node:fs');",
    "const cp = require('node:child_process');",
    "const refs = JSON.parse(Buffer.from(process.env.REFS_B64 || '', 'base64').toString('utf8'));",
    "const deployment = JSON.parse(fs.readFileSync('/tmp/agentrun-provider-profile-deploy.json', 'utf8'));",
    "const container = deployment.spec?.template?.spec?.containers?.[0] || {};",
    "const env = new Map((container.env || []).map((e) => [e.name, e.value || null]));",
    "const noProxy = env.get('AGENTRUN_RUNNER_NO_PROXY_EXTRA') || '';",
    "const secretItems = (refs.secrets || []).map((ref) => {",
    "  const out = cp.spawnSync('kubectl', ['-n', ref.namespace, 'get', 'secret', ref.name, '-o', 'json'], { encoding: 'utf8' });",
    "  if (out.status !== 0) return { namespace: ref.namespace, name: ref.name, key: ref.key, present: false, keyPresent: false, valuesPrinted: false };",
    "  const secret = JSON.parse(out.stdout);",
    "  const raw = secret.data?.[ref.key] || '';",
    "  const encodedBytes = raw ? Buffer.byteLength(raw, 'utf8') : 0;",
    "  const encodedFingerprint = raw ? 'sha256:' + require('node:crypto').createHash('sha256').update(raw).digest('hex') : null;",
    "  return { namespace: ref.namespace, name: ref.name, key: ref.key, present: true, keyPresent: Boolean(raw), encodedBytes, encodedFingerprint, valuesPrinted: false };",
    "});",
    "const serviceItems = (refs.providerServices || []).map((target) => {",
    "  const out = cp.spawnSync('kubectl', ['-n', target.namespace, 'get', 'service', target.name, '-o', 'json'], { encoding: 'utf8' });",
    "  if (out.status !== 0) return { ...target, present: false, portPresent: false, valuesPrinted: false };",
    "  const service = JSON.parse(out.stdout);",
    "  const ports = Array.isArray(service.spec?.ports) ? service.spec.ports : [];",
    "  return { ...target, present: true, portPresent: ports.some((item) => Number(item.port) === Number(target.port)), valuesPrinted: false };",
    "});",
    "const secretsReady = secretItems.every((item) => item.keyPresent);",
    "const servicesReady = serviceItems.every((item) => item.present && item.portPresent);",
    "console.log(JSON.stringify({ ok: secretsReady && servicesReady, manager: { deploymentExists: Boolean(deployment.metadata?.name), readyReplicas: deployment.status?.readyReplicas ?? null, replicas: deployment.status?.replicas ?? null, sourceCommit: env.get('AGENTRUN_SOURCE_COMMIT'), envIdentity: env.get('AGENTRUN_ENV_IDENTITY'), runnerEgressProxyUrl: env.get('AGENTRUN_RUNNER_EGRESS_PROXY_URL'), runnerNoProxyExtra: noProxy, noProxyContainsHyue: noProxy.includes('hyueapi.com'), noProxyContainsPikapython: noProxy.includes('api.pikapython.com') || noProxy.includes('.pikapython.com'), valuesPrinted: false }, secrets: { count: secretItems.length, ready: secretsReady, items: secretItems, valuesPrinted: false }, providerServices: { count: serviceItems.length, ready: servicesReady, items: serviceItems, valuesPrinted: false }, runtimeSecretValuesDecoded: false, valuesPrinted: false }));",
    "NODE",
  ].join("\n");
}

function providerProfileRuntimeStatusScript(spec: AgentRunLaneSpec, secrets: readonly AgentRunLaneSecretSpec[]): string {
  const refs = Buffer.from(JSON.stringify({
    secrets: secrets.map((secret) => ({ id: secret.id, targetRef: secret.targetRef })),
    providerServices: providerRuntimeServiceTargets(secrets),
  }), "utf8").toString("base64");
  return [
    "set -eu",
    `namespace=${shQuote(spec.runtime.namespace)}`,
    `deployment=${shQuote(spec.runtime.managerDeployment)}`,
    `refs_b64=${shQuote(refs)}`,
    "kubectl -n \"$namespace\" get deployment \"$deployment\" -o json >/tmp/agentrun-provider-profile-runtime-deploy.json",
    "REFS_B64=\"$refs_b64\" node <<'NODE'",
    "const fs = require('node:fs');",
    "const cp = require('node:child_process');",
    "const refs = JSON.parse(Buffer.from(process.env.REFS_B64 || '', 'base64').toString('utf8'));",
    "const deployment = JSON.parse(fs.readFileSync('/tmp/agentrun-provider-profile-runtime-deploy.json', 'utf8'));",
    "const secretItems = (refs.secrets || []).map((entry) => {",
    "  const ref = entry.targetRef || {};",
    "  const out = cp.spawnSync('kubectl', ['-n', ref.namespace, 'get', 'secret', ref.name, '-o', 'json'], { encoding: 'utf8' });",
    "  if (out.status !== 0) return { id: entry.id || null, namespace: ref.namespace || null, name: ref.name || null, key: ref.key || null, present: false, keyPresent: false, valuesPrinted: false };",
    "  const secret = JSON.parse(out.stdout);",
    "  const data = secret.data && typeof secret.data === 'object' ? secret.data : {};",
    "  const keyPresent = Object.prototype.hasOwnProperty.call(data, ref.key) && typeof data[ref.key] === 'string' && data[ref.key].length > 0;",
    "  return { id: entry.id || null, namespace: ref.namespace || null, name: ref.name || null, key: ref.key || null, present: true, keyPresent, valuesPrinted: false };",
    "});",
    "const serviceItems = (refs.providerServices || []).map((target) => {",
    "  const out = cp.spawnSync('kubectl', ['-n', target.namespace, 'get', 'service', target.name, '-o', 'json'], { encoding: 'utf8' });",
    "  if (out.status !== 0) return { ...target, present: false, portPresent: false, valuesPrinted: false };",
    "  const service = JSON.parse(out.stdout);",
    "  const ports = Array.isArray(service.spec?.ports) ? service.spec.ports : [];",
    "  return { ...target, present: true, portPresent: ports.some((item) => Number(item.port) === Number(target.port)), valuesPrinted: false };",
    "});",
    "const secretsReady = secretItems.length > 0 && secretItems.every((item) => item.keyPresent);",
    "const servicesReady = serviceItems.every((item) => item.present && item.portPresent);",
    "console.log(JSON.stringify({ ok: secretsReady && servicesReady, manager: { deploymentExists: Boolean(deployment.metadata?.name), readyReplicas: deployment.status?.readyReplicas ?? null, replicas: deployment.status?.replicas ?? null, valuesPrinted: false }, secrets: { count: secretItems.length, ready: secretsReady, presentCount: secretItems.filter((item) => item.present).length, keyPresentCount: secretItems.filter((item) => item.keyPresent).length, items: secretItems, valuesPrinted: false }, providerServices: { count: serviceItems.length, ready: servicesReady, items: serviceItems, valuesPrinted: false }, ownerSourceInspected: false, runtimeSecretValuesDecoded: false, valuesPrinted: false }));",
    "NODE",
  ].join("\n");
}

function providerProfileArgoRefreshSyncScript(spec: AgentRunLaneSpec): string {
  return [
    refreshYamlLaneScript(spec),
    `namespace=${shQuote(spec.gitops.argoNamespace)}`,
    `application=${shQuote(spec.gitops.argoApplication)}`,
    "kubectl -n \"$namespace\" patch application \"$application\" --type=merge -p '{\"operation\":{\"sync\":{\"syncStrategy\":{\"hook\":{}}}}}' >/tmp/agentrun-provider-profile-argo-sync.out 2>/tmp/agentrun-provider-profile-argo-sync.err || true",
    "for i in $(seq 1 60); do",
    "  app_json=$(kubectl -n \"$namespace\" get application \"$application\" -o json 2>/dev/null || printf '{}')",
    "  APP_JSON=\"$app_json\" node <<'NODE' >/tmp/agentrun-provider-profile-argo-state.json",
    "const app = JSON.parse(process.env.APP_JSON || '{}');",
    "const sync = app.status?.sync || {};",
    "const health = app.status?.health || {};",
    "const op = app.status?.operationState || null;",
    "console.log(JSON.stringify({sync: sync.status || null, health: health.status || null, revision: sync.revision || null, operationPhase: op?.phase || null, message: op?.message || null}));",
    "NODE",
    "  if node -e \"const s=require('fs').readFileSync('/tmp/agentrun-provider-profile-argo-state.json','utf8'); const j=JSON.parse(s); process.exit(j.sync==='Synced' && j.health==='Healthy' && (!j.operationPhase || j.operationPhase==='Succeeded') ? 0 : 1)\"; then break; fi",
    "  sleep 3",
    "done",
    "cat /tmp/agentrun-provider-profile-argo-state.json | node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{const j=JSON.parse(s||'{}'); console.log(JSON.stringify({ok:j.sync==='Synced' && j.health==='Healthy', ...j, valuesPrinted:false}));})\"",
  ].join("\n");
}
