import { describe, expect, test } from "bun:test";
import { spawnSync } from "node:child_process";
import { rootPath } from "./config";
import { isRenderedCliResult } from "./output";
import { runPlatformObservabilityCommand } from "./platform-infra-observability";
import { resolveTarget } from "./platform-infra-observability/actions";
import { isKubernetesLabelValue, isKubernetesQualifiedName, readObservabilityConfig } from "./platform-infra-observability/config";
import { renderObservabilityPlanFailure } from "./platform-infra-observability/plan-render";
import { prometheusMetaLabel, re2Literal } from "./platform-infra-observability/prometheus";
import { parseCloseoutOptions } from "./platform-infra-observability/options";
import { statusScript } from "./platform-infra-observability/search-script";
import { renderManifest } from "./platform-infra-observability/trace-script";

describe("platform-infra observability progressive disclosure", () => {
  test("default plan is a bounded table while full and raw preserve the complete plan", async () => {
    const compact = await runPlatformObservabilityCommand({} as never, ["plan", "--target", "NC01"]);
    expect(isRenderedCliResult(compact)).toBe(true);
    if (!isRenderedCliResult(compact)) throw new Error("expected rendered plan");
    expect(Buffer.byteLength(compact.renderedText, "utf8")).toBeLessThan(10_240);
    expect(compact.renderedText).toContain("serviceConnections=8");
    expect(compact.renderedText).toContain("configRefs=6/6 missing=0");
    expect(compact.renderedText).toContain("prometheus=enabled service=prometheus targets=NC01");
    expect(compact.renderedText).toContain("scrapeSelector=unidesk.ai/metrics=true");
    expect(compact.renderedText).toContain("--full");
    expect(compact.renderedText).toContain("--raw");

    for (const mode of ["--full", "--raw"]) {
      const expanded = await runPlatformObservabilityCommand({} as never, ["plan", "--target", "NC01", mode]);
      expect(isRenderedCliResult(expanded)).toBe(false);
      expect(expanded).toMatchObject({
        action: "platform-infra-observability-plan",
        mutation: false,
        renderPlan: { target: { id: "NC01", route: "NC01:k3s", namespace: "platform-infra" } },
      });
      expect((expanded as Record<string, any>).renderPlan.instrumentation).toHaveLength(8);
      expect((expanded as Record<string, any>).config.metricsBackend).toMatchObject({ type: "prometheus", enabled: true });
    }
  });

  test("real outer CLI keeps plan below the stdout dump threshold", () => {
    const result = cli(["platform-infra", "observability", "plan", "--target", "NC01"]);
    expect(result.status).toBe(0);
    expect(Buffer.byteLength(result.stdout, "utf8")).toBeLessThan(10_240);
    expect(result.stdout).toContain("platform-infra observability plan (ok)");
    expect(result.stdout).toContain("configRefs=6/6 missing=0");
    expect(result.stdout).not.toContain("outputTruncated");
    expect(result.stdout).not.toContain("/tmp/unidesk-cli-output");
  });

  test("Prometheus manifests and runtime access only apply to YAML-selected targets", async () => {
    const observability = readObservabilityConfig();
    const nc01 = resolveTarget(observability, "NC01");
    const d518 = resolveTarget(observability, "D518");
    const jd01 = resolveTarget(observability, "JD01");
    const nc01Manifest = renderManifest(observability, nc01);
    for (const target of [d518, jd01]) {
      const manifest = renderManifest(observability, target);
      expect(manifest).not.toContain("kind: ClusterRole\nmetadata:\n  name: platform-infra-prometheus");
      expect(manifest).not.toContain("kind: PersistentVolumeClaim\nmetadata:\n  name: prometheus-data");
      expect(manifest).not.toContain("app.kubernetes.io/component: metrics-backend");
      const plan = await runPlatformObservabilityCommand({} as never, ["plan", "--target", target.id]);
      expect(isRenderedCliResult(plan)).toBe(true);
      if (!isRenderedCliResult(plan)) throw new Error("expected rendered plan");
      expect(plan.renderedText).toContain("prometheus=disabled-for-target");
      expect(plan.renderedText).toContain("objects=8");
      const script = statusScript(observability, target, false);
      expect(script).not.toContain("capture_json metrics_deployments");
      expect(script).not.toContain("prometheus-ready");
      const query = await runPlatformObservabilityCommand({} as never, ["metrics-query", "--target", target.id, "--query", "up", "--full"]);
      expect(query).toMatchObject({ metrics: { enabled: false, disposition: "disabled-for-target", targetIds: ["NC01"] } });
    }
    expect(nc01Manifest).toContain("kind: ClusterRole\nmetadata:\n  name: platform-infra-prometheus");
    expect(nc01Manifest).toContain("kind: PersistentVolumeClaim\nmetadata:\n  name: prometheus-data");
  });

  test("Prometheus relabel rules prefer an explicit path and only default when absent", () => {
    const observability = readObservabilityConfig();
    const manifest = renderManifest(observability, resolveTarget(observability, "NC01"));
    const explicitPath = "source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]\n            action: replace\n            target_label: __metrics_path__\n            regex: (/.+)";
    const defaultPath = "source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]\n            action: replace\n            target_label: __metrics_path__\n            regex: ^$\n            replacement: /metrics";
    expect(manifest).toContain("source_labels: [__meta_kubernetes_pod_label_unidesk_ai_metrics]");
    expect(manifest).toContain("regex: '^true$'");
    expect(manifest).toContain(explicitPath);
    expect(manifest).toContain(defaultPath);
    expect(manifest.indexOf(explicitPath)).toBeLessThan(manifest.indexOf(defaultPath));
  });

  test("Prometheus selector rendering uses qualified names, stable meta labels and literal RE2", () => {
    const observability = readObservabilityConfig();
    observability.metricsBackend.discovery.labelSelector = {
      key: "metrics.example.com/release-track",
      value: "v1.2-beta",
    };
    observability.metricsBackend.discovery.annotationKeys.scrape = "prometheus.io/scrape-enabled";
    const manifest = renderManifest(observability, resolveTarget(observability, "NC01"));
    expect(manifest).toContain("__meta_kubernetes_pod_label_metrics_example_com_release_track");
    expect(manifest).toContain("__meta_kubernetes_pod_annotation_prometheus_io_scrape_enabled");
    expect(manifest).toContain("regex: '^v1\\.2-beta$'");
    expect(prometheusMetaLabel("a.b/c-d_e")).toBe("a_b_c_d_e");
    expect(re2Literal("v1.2-beta")).toBe("^v1\\.2-beta$");
    expect(isKubernetesQualifiedName("metrics.example.com/release-track")).toBe(true);
    expect(isKubernetesQualifiedName("metrics.example.com/release/track")).toBe(false);
    expect(isKubernetesQualifiedName("Metrics.example.com/release-track")).toBe(false);
    expect(isKubernetesQualifiedName(`${"a".repeat(64)}.example/release-track`)).toBe(false);
    expect(isKubernetesLabelValue("v1.2-beta")).toBe(true);
  });

  test("query commands expose only scoped help", () => {
    for (const operation of ["search", "trace", "diagnose-code-agent", "metrics-query"]) {
      const result = cli(["platform-infra", "observability", operation, "--help"]);
      expect(result.status).toBe(0);
      const payload = JSON.parse(result.stdout) as Record<string, any>;
      expect(payload.data.scope).toBe(operation);
      expect(payload.data.command).toBe(`platform-infra observability ${operation}`);
      expect(payload.data.usage.every((line: string) => line.includes(`observability ${operation}`))).toBe(true);
      expect(result.stdout).not.toContain("platform-infra sub2api");
      expect(result.stdout).not.toContain("platform-infra langbot");
      expect(result.stdout).not.toContain("outputTruncated");
    }
  });

  test("top observability help is only an operation index", () => {
    const result = cli(["platform-infra", "observability", "--help"]);
    expect(result.status).toBe(0);
    const payload = JSON.parse(result.stdout) as Record<string, any>;
    expect(payload.data.operations.map((item: Record<string, unknown>) => item.name)).toEqual([
      "plan", "apply", "status", "validate", "trace", "search", "diagnose-code-agent", "metrics-query", "closeout",
    ]);
    expect(payload.data.scopedHelp).toContain("<operation> --help");
    expect(payload.data.usage).toBeUndefined();
    expect(result.stdout).not.toContain("platform-infra sub2api");
  });

  test("closeout accepts repeatable parameter-driven evidence without a product-specific default", () => {
    expect(parseCloseoutOptions([
      "--target", "NC01",
      "--query", "database_ready",
      "--query", "migration_ready",
      "--trace-id", "a7a8f10dc985505c74fad89eeb1ba39f",
      "--service", "example-api",
      "--full",
    ])).toEqual({
      targetId: "NC01",
      queries: ["database_ready", "migration_ready"],
      traceId: "a7a8f10dc985505c74fad89eeb1ba39f",
      service: "example-api",
      full: true,
      raw: false,
    });

    const help = cli(["platform-infra", "observability", "closeout", "--help"]);
    expect(help.status).toBe(0);
    const payload = JSON.parse(help.stdout) as Record<string, any>;
    expect(payload.data.command).toBe("platform-infra observability closeout");
    expect(payload.data.boundary).toContain("non-blocking warning");
    expect(help.stdout).not.toContain("pikaoa");
    expect(() => parseCloseoutOptions(["--target", "NC01", "--service", "example-api"])).toThrow("--service requires --trace-id");
    expect(readObservabilityConfig().metricsBackend.query.maxCloseoutQueries).toBe(8);
  });

  test("closeout rejects query fan-out above the YAML budget before remote access", async () => {
    const args = ["closeout", "--target", "NC01"];
    for (let index = 0; index < 9; index += 1) args.push("--query", `metric_${index}`);
    await expect(runPlatformObservabilityCommand({} as never, args)).rejects.toThrow("at most 8 --query values");
  });

  test("configRef failures name the source and a bounded next command", () => {
    const rendered = renderObservabilityPlanFailure(
      new Error("config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01.runtime.namespace is missing segment namespace"),
      "NC01",
    );
    expect(rendered.ok).toBe(false);
    expect(rendered.renderedText).toContain("config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01.runtime.namespace");
    expect(rendered.renderedText).toContain("rg -n lanes config/hwlab-node-lanes.yaml");
    expect(rendered.renderedText).not.toContain("/tmp/unidesk-cli-output");
  });
});

function cli(args: string[]): { status: number | null; stdout: string; stderr: string } {
  const result = spawnSync("bun", ["scripts/cli.ts", ...args], {
    cwd: rootPath(),
    encoding: "utf8",
    timeout: 30_000,
    env: { ...process.env, NO_COLOR: "1" },
  });
  expect(result.stdout).not.toBe("");
  return { status: result.status, stdout: result.stdout, stderr: result.stderr };
}
