#!/usr/bin/env node
import assert from "node:assert/strict";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";

import { readStructuredFile } from "./src/structured-config.mjs";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const namespace = "hwlab-dev";

async function readConfig(relativePath) {
  return readStructuredFile(repoRoot, relativePath);
}

function listItems(document) {
  return document?.kind === "List" ? document.items ?? [] : [document];
}

function serviceIdsFromDeploy(deploy) {
  return (deploy.services ?? []).map((service) => service.serviceId).filter(Boolean);
}

function assertUnique(label, values) {
  assert.equal(new Set(values).size, values.length, `${label} must be unique`);
}

function assertDeployManifestConsistency(deploy) {
  assert.equal(deploy.environment, "dev", "deploy/deploy.yaml environment");
  assert.equal(deploy.namespace, namespace, "deploy/deploy.yaml namespace");
  assert.equal(deploy.profiles?.prod?.enabled, false, "deploy/deploy.yaml prod disabled");

  const serviceIds = serviceIdsFromDeploy(deploy);
  assert.ok(serviceIds.length > 0, "deploy/deploy.yaml services must not be empty");
  assertUnique("deploy/deploy.yaml serviceId", serviceIds);

  const byId = new Set(serviceIds);
  for (const mapping of deploy.k3s?.serviceMappings ?? []) {
    assert.ok(byId.has(mapping.serviceId), `deploy/deploy.yaml k3s serviceMapping ${mapping.name} references a declared service`);
    assert.equal(mapping.namespace, namespace, `${mapping.name} namespace`);
  }

  const v02 = deploy.lanes?.v02;
  if (v02) {
    const envReuseServices = Array.isArray(v02.envReuseServices) ? v02.envReuseServices.filter(Boolean) : [];
    assert.ok(envReuseServices.length > 0, "deploy.lanes.v02.envReuseServices must not be empty");
    assertUnique("deploy.lanes.v02.envReuseServices", envReuseServices);
    for (const serviceId of envReuseServices) {
      assert.ok(byId.has(serviceId), `deploy.lanes.v02.envReuseServices ${serviceId} must be declared in deploy.services`);
      assert.ok(v02.serviceDeclarations?.[serviceId], `deploy.lanes.v02.serviceDeclarations.${serviceId} is required`);
      assert.ok(v02.bootScripts?.[serviceId], `deploy.lanes.v02.bootScripts.${serviceId} is required`);
    }
  }
}

function assertK8sServiceMappings(deploy, services) {
  const serviceDocs = listItems(services).filter((item) => item?.kind === "Service");
  const serviceNames = new Set(serviceDocs.map((item) => item.metadata?.name).filter(Boolean));
  for (const mapping of deploy.k3s?.serviceMappings ?? []) {
    assert.ok(serviceNames.has(mapping.name), `deploy/k8s/base/services.yaml missing Service ${mapping.name}`);
    const service = serviceDocs.find((item) => item.metadata?.name === mapping.name);
    const port = service?.spec?.ports?.find((entry) => entry.name === "http") ?? service?.spec?.ports?.[0];
    assert.equal(port?.port, mapping.port, `${mapping.name} service port`);
    assert.equal(port?.targetPort, mapping.targetPort, `${mapping.name} targetPort`);
  }
}

function assertRuntimeServiceWorkloads(deploy, workloads) {
  const workloadNames = new Set(listItems(workloads).map((item) => item?.metadata?.name).filter(Boolean));
  for (const service of deploy.services ?? []) {
    if (service.externalImage || service.kind === "external") continue;
    assert.ok(workloadNames.has(service.serviceId), `deploy/k8s/base/workloads.yaml missing workload ${service.serviceId}`);
  }
}

export async function readDevM3CardinalityInputs() {
  const [deploy, workloads, services, topology] = await Promise.all([
    readConfig("deploy/deploy.yaml"),
    readConfig("deploy/k8s/base/workloads.yaml"),
    readConfig("deploy/k8s/base/services.yaml"),
    readConfig("fixtures/mvp/m3-hardware-loop/topology.json").catch(() => null)
  ]);

  return { deploy, workloads, services, topology };
}

export function validateDevM3Cardinality({ deploy, workloads, services }) {
  assertDeployManifestConsistency(deploy);
  assertK8sServiceMappings(deploy, services);
  assertRuntimeServiceWorkloads(deploy, workloads);
}

export async function main() {
  validateDevM3Cardinality(await readDevM3CardinalityInputs());
  console.log("validated DEV deploy manifest service mapping consistency");
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  try {
    await main();
  } catch (error) {
    console.error(error instanceof Error ? error.message : String(error));
    process.exitCode = 1;
  }
}
