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

// Moved mechanically from scripts/src/platform-infra-observability.ts:515-623 for #903.

// SPEC: PJ2026-01060501 OTel追踪 draft-2026-06-19-p0.
// Responsibility: YAML-first platform-infra OpenTelemetry tracing control commands.
import { Buffer } from "node:buffer";
import { readFileSync } from "node:fs";
import type { UniDeskConfig } from "../config";
import { rootPath } from "../config";
import type { RenderedCliResult } from "../output";
import {
  compactCapture,
  createYamlFieldReader,
  numberField,
  parseJsonOutput,
  redactSensitiveUnknown,
  shQuote,
  capture,
} from "../platform-infra-ops-library";

import type { ApplyOptions, CommonOptions, ObservabilityConfig, ObservabilityTarget, StatusEndpoint } from "./types";
import { readObservabilityConfig } from "./config";
import { applyScript, statusScript } from "./search-script";
import { compactStatus, configSummary, manifestObjectSummary, policyChecks, statusSummary, targetSummary } from "./summary";
import { renderManifest } from "./trace-script";
import { apiPathField, configLabel, kubernetesNameField, stringField } from "./types";

export function parseStatusEndpoint(record: Record<string, unknown>, index: number): StatusEndpoint {
  const path = `probes.statusEndpoints[${index}]`;
  return {
    name: stringField(record, "name", path),
    service: kubernetesNameField(record, "service", path),
    portName: stringField(record, "portName", path),
    path: apiPathField(record, "path", path),
  };
}

export function assertKnownEnabledTarget(targets: ObservabilityTarget[], targetId: string, path: string): void {
  const target = targets.find((item) => item.id.toLowerCase() === targetId.toLowerCase());
  if (target === undefined) throw new Error(`${configLabel}.${path} references unknown target ${targetId}; known targets: ${targets.map((item) => item.id).join(", ")}`);
  if (!target.enabled) throw new Error(`${configLabel}.${path} references disabled target ${target.id}`);
}

export function resolveTarget(observability: ObservabilityConfig, targetId: string | null): ObservabilityTarget {
  const resolved = targetId ?? observability.defaults.targetId;
  const target = observability.targets.find((item) => item.id.toLowerCase() === resolved.toLowerCase());
  if (target === undefined) throw new Error(`unknown observability target ${resolved}; known targets: ${observability.targets.map((item) => item.id).join(", ")}`);
  if (!target.enabled) throw new Error(`observability target ${target.id} is disabled in ${configLabel}`);
  return target;
}

export function plan(options: CommonOptions): Record<string, unknown> {
  const observability = readObservabilityConfig();
  const target = resolveTarget(observability, options.targetId);
  const yaml = renderManifest(observability, target);
  const policy = policyChecks(yaml, target);
  return {
    ok: policy.every((check) => check.ok),
    action: "platform-infra-observability-plan",
    mutation: false,
    config: configSummary(observability, target),
    renderPlan: {
      target: targetSummary(target),
      objects: manifestObjectSummary(yaml),
      otlp: {
        collectorGrpcEndpoint: `${observability.collector.serviceName}.${target.namespace}.svc.cluster.local:${observability.collector.otlp.grpcPort}`,
        collectorHttpEndpoint: `http://${observability.collector.serviceName}.${target.namespace}.svc.cluster.local:${observability.collector.otlp.httpPort}`,
        backendGrpcEndpoint: `${observability.traceBackend.serviceName}.${target.namespace}.svc.cluster.local:${observability.traceBackend.otlp.grpcPort}`,
      },
      instrumentation: observability.instrumentation.serviceConnections,
      resourceAttributes: observability.resourceAttributes,
    },
    policy,
    next: {
      dryRun: `bun scripts/cli.ts platform-infra observability apply --target ${target.id} --dry-run`,
      apply: `bun scripts/cli.ts platform-infra observability apply --target ${target.id} --confirm`,
      status: `bun scripts/cli.ts platform-infra observability status --target ${target.id}`,
      validate: `bun scripts/cli.ts platform-infra observability validate --target ${target.id}`,
      trace: `bun scripts/cli.ts platform-infra observability trace --target ${target.id} --trace-id <traceId>`,
    },
  };
}

export async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
  const observability = readObservabilityConfig();
  const target = resolveTarget(observability, options.targetId);
  const yaml = renderManifest(observability, target);
  const policy = policyChecks(yaml, target);
  if (!policy.every((check) => check.ok)) return { ok: false, action: "platform-infra-observability-apply", mode: "policy-blocked", policy };
  const result = await capture(config, target.route, ["sh"], applyScript({
    yaml,
    target,
    dryRun: options.dryRun,
    wait: options.wait,
    collectorDeploymentName: observability.collector.deploymentName,
    backendDeploymentName: observability.traceBackend.deploymentName,
  }));
  const parsed = parseJsonOutput(result.stdout);
  return {
    ok: result.exitCode === 0 && parsed?.ok === true,
    action: "platform-infra-observability-apply",
    mode: options.dryRun ? "dry-run" : "confirmed",
    mutation: !options.dryRun,
    target: targetSummary(target),
    policy,
    remote: parsed ?? compactCapture(result, { full: true }),
  };
}

export async function status(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
  const observability = readObservabilityConfig();
  const target = resolveTarget(observability, options.targetId);
  const result = await capture(config, target.route, ["sh"], statusScript(observability, target, options.full));
  const parsed = parseJsonOutput(result.stdout);
  const summary = parsed === null ? null : statusSummary(parsed);
  return {
    ok: result.exitCode === 0 && summary?.ready === true,
    action: "platform-infra-observability-status",
    mutation: false,
    target: targetSummary(target),
    config: configSummary(observability, target),
    summary,
    remote: options.raw ? parsed : compactStatus(parsed, options.full) ?? compactCapture(result, { full: true }),
    next: {
      plan: `bun scripts/cli.ts platform-infra observability plan --target ${target.id}`,
      apply: `bun scripts/cli.ts platform-infra observability apply --target ${target.id} --confirm`,
      validate: `bun scripts/cli.ts platform-infra observability validate --target ${target.id}`,
    },
  };
}

export async function validate(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
  const observability = readObservabilityConfig();
  const target = resolveTarget(observability, options.targetId);
  const result = await capture(config, target.route, ["sh"], statusScript(observability, target, options.full, true));
  const parsed = parseJsonOutput(result.stdout);
  const summary = parsed === null ? null : statusSummary(parsed);
  const ready = summary?.ready === true;
  const validationTrace = parsed !== null && typeof parsed.validationTrace === "object" && parsed.validationTrace !== null && !Array.isArray(parsed.validationTrace)
    ? parsed.validationTrace as Record<string, unknown>
    : null;
  const validationTraceId = typeof validationTrace?.traceId === "string" ? validationTrace.traceId : null;
  const validationTraceOk = validationTrace?.ok === true;
  return {
    ok: result.exitCode === 0 && ready && validationTraceOk,
    action: "platform-infra-observability-validate",
    mutation: false,
    target: targetSummary(target),
    config: configSummary(observability, target),
    summary,
    validation: {
      readiness: ready ? "passed" : "failed",
      testTrace: validationTrace ?? "not-generated-runtime-not-ready",
      traceQuery: validationTraceId !== null
        ? `bun scripts/cli.ts platform-infra observability trace --target ${target.id} --trace-id ${validationTraceId}`
        : "blocked-until-validation-trace-generated",
      metricsBoundary: "Prometheus/RUM remains outside this trace readiness check.",
    },
    remote: options.raw ? parsed : compactStatus(parsed, options.full) ?? compactCapture(result, { full: true }),
  };
}
