import { readFileSync } from "node:fs";
import { rootPath } from "./config";

export const K3S_CLUSTER_CONFIG_LABEL = "config/platform-infra/k3s-clusters.yaml";

export interface K3sClusterTarget {
  id: string;
  clusterName: string;
  node: {
    id: string;
    bootstrapConfigRef: string;
    hostProxyConfigRef: string;
    hostProxyTargetId: string;
    nodeName: string;
    labels: Record<string, string>;
    windowsRoute: string;
    guestAddress: string;
    guestUser: string;
    stateDir: string;
    k3sVersion: string;
  };
  k3s: {
    writeKubeconfigMode: "0600" | "0640" | "0644";
    maxPods: number;
    disableComponents: string[];
    registryMirror: {
      source: string;
      endpoint: string;
      auth: {
        sourceRef: string;
        usernameKey: string;
        passwordKey: string;
        passwordHashKey: string;
      };
    };
    registryPullSmoke: {
      image: string;
      expectedDigest: string;
      expectedBytes: number;
      concurrentPulls: number;
      timeoutSeconds: number;
    };
  };
}

export function readK3sClusterConfig(targetId?: string): {
  defaultTargetId: string;
  target: K3sClusterTarget;
} {
  const root = record(Bun.YAML.parse(readFileSync(rootPath(K3S_CLUSTER_CONFIG_LABEL), "utf8")), K3S_CLUSTER_CONFIG_LABEL);
  if (root.kind !== "platform-infra-k3s-clusters") throw new Error(`${K3S_CLUSTER_CONFIG_LABEL}.kind must be platform-infra-k3s-clusters`);
  const defaults = record(root.defaults, `${K3S_CLUSTER_CONFIG_LABEL}.defaults`);
  const defaultTargetId = text(defaults, "targetId", `${K3S_CLUSTER_CONFIG_LABEL}.defaults`);
  const id = targetId ?? defaultTargetId;
  const raw = record(record(root.targets, `${K3S_CLUSTER_CONFIG_LABEL}.targets`)[id], `${K3S_CLUSTER_CONFIG_LABEL}.targets.${id}`);
  if (raw.enabled !== true) throw new Error(`${K3S_CLUSTER_CONFIG_LABEL}.targets.${id}.enabled must be true`);
  const node = record(raw.node, `targets.${id}.node`);
  const k3s = record(raw.k3s, `targets.${id}.k3s`);
  const mirror = record(k3s.registryMirror, `targets.${id}.k3s.registryMirror`);
  const mirrorAuth = record(mirror.auth, `targets.${id}.k3s.registryMirror.auth`);
  const pullSmoke = record(k3s.registryPullSmoke, `targets.${id}.k3s.registryPullSmoke`);
  const bootstrap = resolveRef(text(node, "bootstrapConfigRef", `targets.${id}.node`));
  const host = record(bootstrap.host, "node bootstrap host");
  const network = record(bootstrap.network, "node bootstrap network");
  const ubuntu = record(bootstrap.ubuntu, "node bootstrap ubuntu");
  const bootstrapK3s = record(bootstrap.k3s, "node bootstrap k3s");
  const hostProxyConfigRef = text(node, "hostProxyConfigRef", `targets.${id}.node`);
  return {
    defaultTargetId,
    target: {
      id,
      clusterName: kubernetesName(raw, "clusterName", `targets.${id}`),
      node: {
        id: text(node, "id", `targets.${id}.node`),
        bootstrapConfigRef: text(node, "bootstrapConfigRef", `targets.${id}.node`),
        hostProxyConfigRef,
        hostProxyTargetId: hostProxyTargetId(hostProxyConfigRef),
        nodeName: kubernetesName(node, "nodeName", `targets.${id}.node`),
        labels: stringMap(record(node.labels, `targets.${id}.node.labels`), `targets.${id}.node.labels`),
        windowsRoute: text(host, "route", "node bootstrap host"),
        guestAddress: text(network, "address", "node bootstrap network"),
        guestUser: text(ubuntu, "user", "node bootstrap ubuntu"),
        stateDir: text(host, "stateDir", "node bootstrap host"),
        k3sVersion: text(bootstrapK3s, "version", "node bootstrap k3s"),
      },
      k3s: {
        writeKubeconfigMode: enumText(k3s, "writeKubeconfigMode", `targets.${id}.k3s`, ["0600", "0640", "0644"]),
        maxPods: integer(k3s, "maxPods", `targets.${id}.k3s`),
        disableComponents: stringArray(k3s.disableComponents, `targets.${id}.k3s.disableComponents`),
        registryMirror: {
          source: text(mirror, "source", "registryMirror"),
          endpoint: httpsUrl(mirror, "endpoint", "registryMirror"),
          auth: {
            sourceRef: text(mirrorAuth, "sourceRef", "registryMirror.auth"),
            usernameKey: text(mirrorAuth, "usernameKey", "registryMirror.auth"),
            passwordKey: text(mirrorAuth, "passwordKey", "registryMirror.auth"),
            passwordHashKey: text(mirrorAuth, "passwordHashKey", "registryMirror.auth"),
          },
        },
        registryPullSmoke: {
          image: text(pullSmoke, "image", "registryPullSmoke"),
          expectedDigest: sha256Digest(pullSmoke, "expectedDigest", "registryPullSmoke"),
          expectedBytes: integer(pullSmoke, "expectedBytes", "registryPullSmoke"),
          concurrentPulls: integer(pullSmoke, "concurrentPulls", "registryPullSmoke"),
          timeoutSeconds: integer(pullSmoke, "timeoutSeconds", "registryPullSmoke"),
        },
      },
    },
  };
}

export function clusterServerArgs(configRef: string): string[] {
  throw new Error(`external k3s cluster attachment is retired; remove clusterConfigRef: ${configRef}`);
}

function resolveRef(value: string): Record<string, unknown> {
  const [file, path] = value.split("#", 2);
  if (!file || !path) throw new Error(`configRef must contain file#path: ${value}`);
  let current: unknown = Bun.YAML.parse(readFileSync(rootPath(file), "utf8"));
  for (const segment of path.split(".")) current = record(current, value)[segment];
  return record(current, value);
}

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

function text(value: Record<string, unknown>, key: string, label: string): string {
  const result = value[key];
  if (typeof result !== "string" || result.length === 0) throw new Error(`${label}.${key} must be a non-empty string`);
  return result;
}

function integer(value: Record<string, unknown>, key: string, label: string): number {
  const result = value[key];
  if (!Number.isInteger(result) || Number(result) <= 0) throw new Error(`${label}.${key} must be a positive integer`);
  return Number(result);
}

function httpsUrl(value: Record<string, unknown>, key: string, label: string): string {
  const result = text(value, key, label);
  const parsed = new URL(result);
  if (parsed.protocol !== "https:" || parsed.username.length > 0 || parsed.password.length > 0) {
    throw new Error(`${label}.${key} must be an HTTPS URL without embedded credentials`);
  }
  return result;
}

function sha256Digest(value: Record<string, unknown>, key: string, label: string): string {
  const result = text(value, key, label);
  if (!/^sha256:[0-9a-f]{64}$/u.test(result)) throw new Error(`${label}.${key} must be a sha256 digest`);
  return result;
}

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

function kubernetesName(value: Record<string, unknown>, key: string, label: string): string {
  const result = text(value, key, label);
  if (!/^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/u.test(result) || result.length > 63) {
    throw new Error(`${label}.${key} must be a Kubernetes DNS label`);
  }
  return result;
}

function stringMap(value: Record<string, unknown>, label: string): Record<string, string> {
  return Object.fromEntries(Object.entries(value).map(([key, item]) => {
    if (typeof item !== "string" || item.length === 0) throw new Error(`${label}.${key} must be a non-empty string`);
    return [key, item];
  }));
}

function stringArray(value: unknown, label: string): string[] {
  if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== "string" || item.length === 0)) {
    throw new Error(`${label} must be a non-empty string array`);
  }
  return value as string[];
}

function hostProxyTargetId(ref: string): string {
  const match = ref.match(/^config\/platform-infra\/host-proxy\.yaml#targets\.([A-Za-z0-9._-]+)$/u);
  if (match === null) throw new Error(`hostProxyConfigRef must point at config/platform-infra/host-proxy.yaml#targets.<id>`);
  return match[1] ?? "";
}
