// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. trace-script module for scripts/src/platform-infra-observability.ts.

// Moved mechanically from scripts/src/platform-infra-observability.ts:1575-1813 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 { ObservabilityConfig, ObservabilityTarget } from "./types";
import { tempoService } from "./search-script";
import { imageReference, indent } from "./summary";
import { prometheusManifests } from "./prometheus";

export function asArray(value: unknown): unknown[] {
  return Array.isArray(value) ? value : [];
}

export function limitArray(value: unknown, limit: number): unknown[] {
  return asArray(value).slice(0, limit);
}

export function renderManifest(observability: ObservabilityConfig, target: ObservabilityTarget): string {
  const collectorImage = imageReference(observability.images.collector);
  const tempoImage = imageReference(observability.images.tempo);
  return [
    target.createNamespace ? namespaceManifest(target) : "",
    allowAllNetworkPolicy(target),
    collectorConfigMap(observability, target),
    collectorDeployment(observability, target, collectorImage),
    collectorService(observability, target),
    tempoConfigMap(observability, target),
    tempoDeployment(observability, target, tempoImage),
    tempoService(observability, target),
    ...prometheusManifests(observability, target),
  ].filter((item) => item.trim().length > 0).join("\n---\n");
}

export function namespaceManifest(target: ObservabilityTarget): string {
  return `apiVersion: v1
kind: Namespace
metadata:
  name: ${target.namespace}
  labels:
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
    unidesk.ai/runtime-node: ${target.id}
`;
}

export function allowAllNetworkPolicy(target: ObservabilityTarget): string {
  return `apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
  namespace: ${target.namespace}
  labels:
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - {}
  egress:
    - {}
`;
}

export function collectorConfigMap(observability: ObservabilityConfig, target: ObservabilityTarget): string {
  const config = `receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:${observability.collector.otlp.grpcPort}
      http:
        endpoint: 0.0.0.0:${observability.collector.otlp.httpPort}
processors:
  batch: {}
exporters:
  otlp/tempo:
    endpoint: ${observability.traceBackend.serviceName}.${target.namespace}.svc.cluster.local:${observability.traceBackend.otlp.grpcPort}
    tls:
      insecure: true
extensions:
  health_check:
    endpoint: 0.0.0.0:${observability.collector.healthPort}
service:
  extensions: [health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/tempo]
`;
  return `apiVersion: v1
kind: ConfigMap
metadata:
  name: ${observability.collector.configMapName}
  namespace: ${target.namespace}
  labels:
    app.kubernetes.io/name: ${observability.collector.deploymentName}
    app.kubernetes.io/component: tracing
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
  annotations:
    unidesk.ai/spec: "${observability.metadata.spec}"
data:
  collector.yaml: |
${indent(config, 4)}
`;
}

export function collectorDeployment(observability: ObservabilityConfig, target: ObservabilityTarget, image: string): string {
  return `apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${observability.collector.deploymentName}
  namespace: ${target.namespace}
  labels:
    app.kubernetes.io/name: ${observability.collector.deploymentName}
    app.kubernetes.io/component: tracing
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
spec:
  replicas: ${observability.collector.replicas}
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ${observability.collector.deploymentName}
      app.kubernetes.io/component: tracing
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ${observability.collector.deploymentName}
        app.kubernetes.io/component: tracing
        app.kubernetes.io/part-of: platform-infra
      annotations:
        unidesk.ai/spec: "${observability.metadata.spec}"
    spec:
      containers:
        - name: collector
          image: ${image}
          imagePullPolicy: ${observability.images.collector.pullPolicy}
          args:
            - --config=/etc/otelcol/collector.yaml
          ports:
            - name: otlp-grpc
              containerPort: ${observability.collector.otlp.grpcPort}
            - name: otlp-http
              containerPort: ${observability.collector.otlp.httpPort}
            - name: health
              containerPort: ${observability.collector.healthPort}
          readinessProbe:
            httpGet:
              path: /
              port: health
          volumeMounts:
            - name: config
              mountPath: /etc/otelcol/collector.yaml
              subPath: collector.yaml
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: ${observability.collector.configMapName}
`;
}

export function collectorService(observability: ObservabilityConfig, target: ObservabilityTarget): string {
  return `apiVersion: v1
kind: Service
metadata:
  name: ${observability.collector.serviceName}
  namespace: ${target.namespace}
  labels:
    app.kubernetes.io/name: ${observability.collector.deploymentName}
    app.kubernetes.io/component: tracing
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: ${observability.collector.deploymentName}
    app.kubernetes.io/component: tracing
  ports:
    - name: otlp-grpc
      port: ${observability.collector.otlp.grpcPort}
      targetPort: otlp-grpc
    - name: otlp-http
      port: ${observability.collector.otlp.httpPort}
      targetPort: otlp-http
    - name: health
      port: ${observability.collector.healthPort}
      targetPort: health
`;
}

export function tempoConfigMap(observability: ObservabilityConfig, _target: ObservabilityTarget): string {
  const config = `server:
  http_listen_port: ${observability.traceBackend.httpPort}
distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:${observability.traceBackend.otlp.grpcPort}
        http:
          endpoint: 0.0.0.0:${observability.traceBackend.otlp.httpPort}
ingester:
  trace_idle_period: 10s
  max_block_duration: 5m
compactor:
  compaction:
    block_retention: ${observability.traceBackend.storage.retention}
storage:
  trace:
    backend: local
    wal:
      path: /var/tempo/wal
    local:
      path: /var/tempo/traces
`;
  return `apiVersion: v1
kind: ConfigMap
metadata:
  name: ${observability.traceBackend.configMapName}
  namespace: ${_target.namespace}
  labels:
    app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
    app.kubernetes.io/component: trace-backend
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
  annotations:
    unidesk.ai/spec: "${observability.metadata.spec}"
data:
  tempo.yaml: |
${indent(config, 4)}
`;
}

export function tempoDeployment(observability: ObservabilityConfig, target: ObservabilityTarget, image: string): string {
  return `apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${observability.traceBackend.deploymentName}
  namespace: ${target.namespace}
  labels:
    app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
    app.kubernetes.io/component: trace-backend
    app.kubernetes.io/part-of: platform-infra
    app.kubernetes.io/managed-by: unidesk
spec:
  replicas: ${observability.traceBackend.replicas}
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
      app.kubernetes.io/component: trace-backend
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
        app.kubernetes.io/component: trace-backend
        app.kubernetes.io/part-of: platform-infra
      annotations:
        unidesk.ai/spec: "${observability.metadata.spec}"
    spec:
      containers:
        - name: tempo
          image: ${image}
          imagePullPolicy: ${observability.images.tempo.pullPolicy}
          args:
            - -config.file=/etc/tempo/tempo.yaml
          ports:
            - name: http
              containerPort: ${observability.traceBackend.httpPort}
            - name: otlp-grpc
              containerPort: ${observability.traceBackend.otlp.grpcPort}
            - name: otlp-http
              containerPort: ${observability.traceBackend.otlp.httpPort}
          readinessProbe:
            httpGet:
              path: ${observability.probes.readinessPath}
              port: http
          volumeMounts:
            - name: config
              mountPath: /etc/tempo/tempo.yaml
              subPath: tempo.yaml
              readOnly: true
            - name: data
              mountPath: /var/tempo
      volumes:
        - name: config
          configMap:
            name: ${observability.traceBackend.configMapName}
        - name: data
          emptyDir: {}
`;
}
