// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
// Responsibility: shared WebProbe observe action configuration helpers.
import { readFileSync } from "node:fs";
import { repoRoot, rootPath } from "../config";
import { runCommand } from "../command";
import { hwlabRuntimeLaneConfigPath, type HwlabRuntimeLaneSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeAuthLoginSpec, type HwlabRuntimeWebProbeBrowserFreezePolicySpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes";

import type { WebProbeBrowserProxyMode } from "./entry";
import { transPath } from "./runtime-common";
import { record, shellQuote } from "./utils";

export function webObserveGcDefaultKeepHours(): number {
  const configPath = "config/unidesk-cli.yaml";
  const parsed = record(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")) as unknown);
  const gc = record(parsed.gc);
  const scratch = record(gc.stateStaleScratch);
  const keepHours = scratch.keepHours;
  if (!Number.isInteger(keepHours) || keepHours < 0) {
    throw new Error(`${configPath}#gc.stateStaleScratch.keepHours must be a non-negative integer`);
  }
  return keepHours;
}

export function nodeWebProbeAlertThresholds(spec: HwlabRuntimeLaneSpec): HwlabRuntimeWebProbeAlertThresholdsSpec {
  const thresholds = spec.webProbe?.alertThresholds;
  if (thresholds === undefined) {
    throw new Error(`${hwlabRuntimeLaneConfigPath()} node=${spec.nodeId} lane=${spec.lane} requires webProbe.alertThresholds for web-probe observe`);
  }
  return thresholds;
}

export function nodeWebProbeBrowserFreezePolicy(spec: HwlabRuntimeLaneSpec): HwlabRuntimeWebProbeBrowserFreezePolicySpec {
  const policy = spec.webProbe?.browserFreezePolicy;
  if (policy === undefined) {
    throw new Error(`${hwlabRuntimeLaneConfigPath()} node=${spec.nodeId} lane=${spec.lane} requires webProbe.browserFreezePolicy for web-probe observe`);
  }
  return policy;
}

export function nodeWebProbeProjectManagementConfig(spec: HwlabRuntimeLaneSpec): HwlabRuntimeWebProbeProjectManagementSpec | null {
  return spec.webProbe?.projectManagement ?? null;
}

export function nodeWebProbeAuthLoginConfig(spec: HwlabRuntimeLaneSpec): HwlabRuntimeWebProbeAuthLoginSpec | null {
  return spec.webProbe?.authLogin ?? null;
}

export interface NodeWebProbeHostProxyEnv {
  readonly envAssignments: string[];
  readonly summary: Record<string, unknown>;
}

export function nodeWebProbeHostProxyEnv(spec: HwlabRuntimeLaneSpec, browserProxyMode: WebProbeBrowserProxyMode = "auto"): NodeWebProbeHostProxyEnv {
  if (browserProxyMode === "direct") {
    return {
      envAssignments: [],
      summary: {
        source: "option",
        mode: "direct",
        networkProfileId: spec.networkProfileId,
        proxy: { enabled: false },
        valuesPrinted: false,
      },
    };
  }
  const proxy = spec.networkProfile.proxy;
  const serviceCache = new Map<string, string>();
  const http = resolveNodeWebProbeHostProxyUrl(spec, proxy.http, serviceCache);
  const https = resolveNodeWebProbeHostProxyUrl(spec, proxy.https, serviceCache);
  const all = resolveNodeWebProbeHostProxyUrl(spec, proxy.all, serviceCache);
  const noProxy = proxy.noProxy.join(",");
  return {
    envAssignments: [
      ["HTTP_PROXY", http.url],
      ["HTTPS_PROXY", https.url],
      ["ALL_PROXY", all.url],
      ["http_proxy", http.url],
      ["https_proxy", https.url],
      ["all_proxy", all.url],
      ["NO_PROXY", noProxy],
      ["no_proxy", noProxy],
    ].map(([key, value]) => `${key}=${shellQuote(value)}`),
    summary: {
      source: "yaml",
      mode: "host-env",
      networkProfileId: spec.networkProfileId,
      proxy: {
        http: http.summary,
        https: https.summary,
        all: all.summary,
        noProxyCount: proxy.noProxy.length,
      },
      valuesPrinted: false,
    },
  };
}

export function resolveNodeWebProbeHostProxyUrl(
  spec: HwlabRuntimeLaneSpec,
  rawUrl: string,
  serviceCache: Map<string, string>,
): { url: string; summary: Record<string, unknown> } {
  let parsed: URL;
  try {
    parsed = new URL(rawUrl);
  } catch (error) {
    throw new Error(`config/hwlab-node-lanes.yaml networkProfiles.${spec.networkProfileId}.proxy contains invalid proxy URL: ${error instanceof Error ? error.message : String(error)}`);
  }
  const service = parseKubernetesServiceDnsHost(parsed.hostname);
  if (service === null) {
    return {
      url: rawUrl,
      summary: {
        mode: "host-url",
        host: parsed.hostname,
        port: parsed.port || null,
        valuesPrinted: false,
      },
    };
  }
  const clusterIp = resolveKubernetesServiceClusterIp(spec, service.namespace, service.name, serviceCache);
  const originalHost = parsed.hostname;
  parsed.hostname = clusterIp;
  const resolvedUrl = normalizedProxyUrl(parsed);
  return {
    url: resolvedUrl,
    summary: {
      mode: "k8s-service-cluster-ip",
      service: service.name,
      namespace: service.namespace,
      originalHost,
      resolvedHost: clusterIp,
      port: parsed.port || null,
      valuesPrinted: false,
    },
  };
}

export function parseKubernetesServiceDnsHost(hostname: string): { name: string; namespace: string } | null {
  const match = hostname.toLowerCase().match(/^([a-z0-9]([-a-z0-9]*[a-z0-9])?)\.([a-z0-9]([-a-z0-9]*[a-z0-9])?)\.svc(?:\.cluster\.local)?$/u);
  if (match === null) return null;
  return { name: match[1] ?? "", namespace: match[3] ?? "" };
}

export function resolveKubernetesServiceClusterIp(
  spec: HwlabRuntimeLaneSpec,
  namespace: string,
  serviceName: string,
  serviceCache: Map<string, string>,
): string {
  const cacheKey = `${namespace}/${serviceName}`;
  const cached = serviceCache.get(cacheKey);
  if (cached !== undefined) return cached;
  const result = runCommand([transPath(), spec.nodeKubeRoute, "get", "svc", "-n", namespace, serviceName, "-o", "jsonpath={.spec.clusterIP}"], repoRoot, { timeoutMs: 20_000 });
  const clusterIp = result.stdout.trim();
  if (result.exitCode !== 0 || clusterIp.length === 0) {
    const reason = result.stderr.trim().slice(-500) || result.stdout.trim().slice(-500) || `exitCode=${result.exitCode}`;
    throw new Error(`web-probe proxy service resolution failed for ${spec.nodeId}/${spec.lane} ${namespace}/${serviceName}: ${reason}`);
  }
  serviceCache.set(cacheKey, clusterIp);
  return clusterIp;
}

export function normalizedProxyUrl(parsed: URL): string {
  const value = parsed.toString();
  if (parsed.pathname === "/" && parsed.search === "" && parsed.hash === "") return value.replace(/\/$/u, "");
  return value;
}

export function webProbeAccountEnvAssignments(): string[] {
  return Object.entries(process.env)
    .filter(([key, value]) => value !== undefined && /^HWLAB_WEB_(?:ACCOUNT_)?[A-Z0-9_]+_(?:JSON|USER|PASS)$/u.test(key))
    .map(([key, value]) => `${key}=${shellQuote(value ?? "")}`);
}
