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

type JsonRecord = Record<string, unknown>;

const verifiedDecisionCenterCommit = "b5486a61ab0aa6c227366a95d1afa68281584359";
const verifiedFrontendCommit = "7b9aa4261c216586954cdf926ce2c914a9db5ae3";

function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
  if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}

function asRecord(value: unknown, label: string): JsonRecord {
  assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), `${label} must be an object`, value);
  return value as JsonRecord;
}

function asArray(value: unknown, label: string): unknown[] {
  assertCondition(Array.isArray(value), `${label} must be an array`, value);
  return value as unknown[];
}

function findService(environment: "dev" | "prod", serviceId: string): JsonRecord {
  const manifest = asRecord(JSON.parse(readFileSync(rootPath("deploy.json"), "utf8")) as unknown, "deploy.json");
  const environments = asRecord(manifest.environments, "deploy.json.environments");
  const env = asRecord(environments[environment], `deploy.json.environments.${environment}`);
  const services = asArray(env.services, `deploy.json.environments.${environment}.services`);
  const service = services.map((item, index) => asRecord(item, `${environment}.services[${index}]`))
    .find((item) => item.id === serviceId);
  assertCondition(service !== undefined, `${environment}/${serviceId} must exist in deploy.json`);
  return service as JsonRecord;
}

function runDeployPlan(environment: "dev" | "prod", serviceId: string): JsonRecord {
  const result = spawnSync("bun", [
    "scripts/cli.ts",
    "artifact-registry",
    "deploy-service",
    "--env",
    environment,
    "--service",
    serviceId,
    "--commit",
    verifiedDecisionCenterCommit,
    "--dry-run",
  ], {
    cwd: rootPath(),
    encoding: "utf8",
    maxBuffer: 8 * 1024 * 1024,
  });
  assertCondition(result.status === 0, `artifact consumer dry-run should exit 0 for ${environment}/${serviceId}`, {
    status: result.status,
    stdout: result.stdout.slice(-2000),
    stderr: result.stderr.slice(-2000),
  });
  const envelope = asRecord(JSON.parse(result.stdout) as unknown, "artifact consumer envelope");
  return asRecord(envelope.data, "artifact consumer dry-run data");
}

function assertNoBuildK3sDecisionCenter(environment: "dev" | "prod", expectedDeployment: string, expectedNamespace: string): void {
  const service = findService(environment, "decision-center");
  assertCondition(service.commitId === verifiedDecisionCenterCommit, `${environment}/decision-center desired commit must match verified live commit`, service);

  const plan = runDeployPlan(environment, "decision-center");
  const registry = asRecord(plan.registry, `${environment}/decision-center registry`);
  const build = asRecord(plan.build, `${environment}/decision-center build`);
  const target = asRecord(plan.target, `${environment}/decision-center target`);

  assertCondition(plan.ok === true && plan.dryRun === true && plan.mutation === false, `${environment}/decision-center dry-run must be non-mutating`, plan);
  assertCondition(plan.commit === verifiedDecisionCenterCommit, `${environment}/decision-center dry-run commit mismatch`, plan);
  assertCondition(plan.serviceId === "decision-center", `${environment}/decision-center service id mismatch`, plan);
  assertCondition(plan.sourceImage === `127.0.0.1:5000/unidesk/decision-center:${verifiedDecisionCenterCommit}`, `${environment}/decision-center source image mismatch`, plan);
  assertCondition(registry.repository === "unidesk/decision-center", `${environment}/decision-center registry repository mismatch`, registry);
  assertCondition(registry.tag === verifiedDecisionCenterCommit, `${environment}/decision-center registry tag mismatch`, registry);
  assertCondition(build.producerBoundary === "ci publish-user-service", `${environment}/decision-center producer boundary mismatch`, build);
  assertCondition(build.willCompile === false, `${environment}/decision-center CD must not compile`, build);
  assertCondition(build.willRunDockerBuild === false, `${environment}/decision-center CD must not docker build`, build);
  assertCondition(build.willRunDockerComposeBuild === false, `${environment}/decision-center CD must not compose build`, build);
  assertCondition(target.kind === "d601-k3s", `${environment}/decision-center target must be D601 k3s`, target);
  assertCondition(target.namespace === expectedNamespace, `${environment}/decision-center namespace mismatch`, target);
  assertCondition(target.deployment === expectedDeployment, `${environment}/decision-center deployment mismatch`, target);
  assertCondition(String(target.deployCommandShape ?? "").includes("kubectl set image"), `${environment}/decision-center command shape must be k3s artifact update`, target);
  assertCondition(!JSON.stringify(plan).includes("server rebuild"), `${environment}/decision-center plan must not mention server rebuild`, plan);
}

for (const environment of ["dev", "prod"] as const) {
  const frontend = findService(environment, "frontend");
  assertCondition(frontend.commitId === verifiedFrontendCommit, `${environment}/frontend desired commit must stay aligned to verified UI artifact`, frontend);
}

assertNoBuildK3sDecisionCenter("dev", "decision-center-dev", "unidesk-dev");
assertNoBuildK3sDecisionCenter("prod", "decision-center", "unidesk");

process.stdout.write(`${JSON.stringify({
  ok: true,
  verifiedDecisionCenterCommit,
  verifiedFrontendCommit,
  checks: [
    "decision-center dev/prod desired commits match the verified live/artifact commit",
    "frontend dev/prod desired commits remain aligned to the same verified UI artifact",
    "decision-center dev/prod dry-run plans are D601 k3s artifact consumers",
    "decision-center dry-run plans declare no compile, docker build, compose build, or server rebuild path",
  ],
}, null, 2)}\n`);
