import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { repoRoot } from "./config";
import { materializeYamlComposition } from "./yaml-composition";

export const transHostProxyConfigPath = "config/platform-infra/host-proxy.yaml";

export type TransHostProxyEnvApplyTo = "host-posix-commands";

export interface TransHostProxyEnvRule {
  targetId: string;
  enabled: boolean;
  envFile: string;
  envFileRef: "files.envFile";
  applyTo: TransHostProxyEnvApplyTo;
  configPath: string;
  configPathLabel: string;
}

export function readTransHostProxyEnvRule(targetId: string): TransHostProxyEnvRule | null {
  const absolutePath = join(repoRoot, transHostProxyConfigPath);
  if (!existsSync(absolutePath)) return null;
  const root = record(materializeYamlComposition(Bun.YAML.parse(readFileSync(absolutePath, "utf8")) as unknown, { label: transHostProxyConfigPath }).value, transHostProxyConfigPath);
  if (root.kind !== "platform-infra-host-proxy") throw new Error(`${transHostProxyConfigPath}.kind must be platform-infra-host-proxy`);
  const targets = record(root.targets, `${transHostProxyConfigPath}.targets`);
  const targetRaw = targets[targetId];
  if (targetRaw === undefined) return null;
  const target = record(targetRaw, `${transHostProxyConfigPath}.targets.${targetId}`);
  if (target.trans === undefined) return null;
  const targetEnabled = booleanField(target, "enabled", `${transHostProxyConfigPath}.targets.${targetId}`);
  if (!targetEnabled) return null;
  const trans = record(target.trans, `${transHostProxyConfigPath}#targets.${targetId}.trans`);
  if (trans.hostProxyEnv === undefined) return null;
  const raw = record(trans.hostProxyEnv, `${transHostProxyConfigPath}#targets.${targetId}.trans.hostProxyEnv`);
  const enabled = booleanField(raw, "enabled", `${transHostProxyConfigPath}#targets.${targetId}.trans.hostProxyEnv`);
  if (!enabled) return null;
  const envFileRef = enumField(raw, "envFileRef", `${transHostProxyConfigPath}.targets.${targetId}.trans.hostProxyEnv`, ["files.envFile"] as const);
  const files = record(target.files, `${transHostProxyConfigPath}.targets.${targetId}.files`);
  return {
    targetId,
    enabled,
    envFile: absolutePathField(files, "envFile", `${transHostProxyConfigPath}.targets.${targetId}.files`),
    envFileRef,
    applyTo: enumField(raw, "applyTo", `${transHostProxyConfigPath}#targets.${targetId}.trans.hostProxyEnv`, ["host-posix-commands"] as const),
    configPath: transHostProxyConfigPath,
    configPathLabel: `${transHostProxyConfigPath}#targets.${targetId}.trans.hostProxyEnv`,
  };
}

export function transHostProxyEnvSummary(targetId: string): Record<string, unknown> | null {
  const rule = readTransHostProxyEnvRule(targetId);
  if (rule === null) return null;
  return {
    enabled: rule.enabled,
    envFileRef: rule.envFileRef,
    envFile: rule.envFile,
    applyTo: rule.applyTo,
    source: rule.configPathLabel,
  };
}

function record(value: unknown, label: string): Record<string, unknown> {
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
    throw new Error(`${label} must be an object`);
  }
  return value as Record<string, unknown>;
}

function booleanField(raw: Record<string, unknown>, key: string, label: string): boolean {
  const value = raw[key];
  if (typeof value !== "boolean") throw new Error(`${label}.${key} must be a boolean`);
  return value;
}

function enumField<const T extends readonly string[]>(raw: Record<string, unknown>, key: string, label: string, allowed: T): T[number] {
  const value = raw[key];
  if (typeof value !== "string" || !(allowed as readonly string[]).includes(value)) {
    throw new Error(`${label}.${key} must be one of: ${allowed.join(", ")}`);
  }
  return value as T[number];
}

function absolutePathField(raw: Record<string, unknown>, key: string, label: string): string {
  const value = raw[key];
  if (typeof value !== "string" || !value.startsWith("/")) throw new Error(`${label}.${key} must be an absolute path`);
  if (value.includes("\0") || value.includes("\n")) throw new Error(`${label}.${key} must not contain control characters`);
  return value;
}
