import { readFileSync } from "node:fs";
import { rootPath } from "./src/config";
import { runArtifactRegistryCommand } from "./src/artifact-registry";

const serviceId = "claudeqq";
const desiredCommit = "203b1f46684c91340ecbbd8a74502bd55e4f2011";
const sourceRepo = "https://gitee.com/lyon1998/agent_skills";
const dockerfile = "claudeqq/Dockerfile";
const eventPaths = ["/api/events/recent", "/api/events/subscriptions"];

type JsonRecord = Record<string, unknown>;

function assertCondition(condition: boolean, message: string): void {
  if (!condition) throw new Error(message);
}

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

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

function stringField(value: unknown, path: string): string {
  assertCondition(typeof value === "string" && value.length > 0, `${path} must be a non-empty string`);
  return value as string;
}

function serviceById(environment: JsonRecord, id: string, path: string): JsonRecord {
  const services = asArray(environment.services, `${path}.services`).map((item, index) => asRecord(item, `${path}.services[${index}]`));
  const service = services.find((item) => item.id === id);
  assertCondition(service !== undefined, `${path}.services must include ${id}`);
  return service!;
}

function assertDeployJson(): void {
  const deploy = asRecord(JSON.parse(readFileSync(rootPath("deploy.json"), "utf8")), "deploy.json");
  const environments = asRecord(deploy.environments, "deploy.json.environments");
  for (const environment of ["dev", "prod"] as const) {
    const service = serviceById(asRecord(environments[environment], `deploy.json.environments.${environment}`), serviceId, `deploy.json.environments.${environment}`);
    assertCondition(service.repo === sourceRepo, `${environment} deploy.json claudeqq repo must match source repo`);
    assertCondition(service.commitId === desiredCommit, `${environment} deploy.json claudeqq commit must match desired commit`);
  }
}

function assertArtifactCatalog(): void {
  const catalog = asRecord(JSON.parse(readFileSync(rootPath("CI.json"), "utf8")), "CI.json");
  const artifacts = asArray(catalog.artifacts, "CI.json.artifacts").map((item, index) => asRecord(item, `CI.json.artifacts[${index}]`));
  const artifact = artifacts.find((item) => item.serviceId === serviceId);
  assertCondition(artifact !== undefined, "CI.json must include claudeqq artifact producer");
  assertCondition(artifact!.kind === "source-build", "claudeqq artifact must be source-build");
  assertCondition(artifact!.status === "supported", "claudeqq artifact must be supported");
  assertCondition(artifact!.producer === "ci publish-user-service", "claudeqq artifact producer must be ci publish-user-service");
  const source = asRecord(artifact!.source, "CI.json claudeqq source");
  assertCondition(source.repo === sourceRepo, "claudeqq artifact source repo must match agent_skills");
  assertCondition(source.dockerfile === dockerfile, "claudeqq artifact dockerfile must be claudeqq/Dockerfile");
  const image = asRecord(artifact!.image, "CI.json claudeqq image");
  assertCondition(image.repository === "unidesk/claudeqq", "claudeqq artifact image repository must be unidesk/claudeqq");
}

function assertAdapterSourceContract(): void {
  const adapter = readFileSync(rootPath("src/components/microservices/claudeqq/adapter.js"), "utf8");
  const imageDockerfile = readFileSync(rootPath("src/components/microservices/claudeqq/Dockerfile"), "utf8");
  for (const eventPath of eventPaths) {
    assertCondition(adapter.includes(eventPath), `adapter must expose ${eventPath}`);
  }
  assertCondition(adapter.includes("claudeqq-event-api-v1"), "adapter health metadata must name the event API contract");
  assertCondition(adapter.includes("adapter-readonly-fallback"), "adapter must include read-only event fallback");
  assertCondition(adapter.includes("CLAUDEQQ_HOST: upstreamHost"), "adapter must bind the upstream server to the private upstream host");
  assertCondition(adapter.includes("CLAUDEQQ_PORT: String(upstreamPort)"), "adapter must bind the upstream server to the private upstream port");
  assertCondition(adapter.includes("POST /api/events/subscriptions"), "adapter health metadata must document event subscription mutation paths");
  assertCondition(imageDockerfile.includes('CMD ["node", "unidesk-adapter.cjs"]'), "claudeqq image must start the UniDesk adapter by default");
}

async function assertDryRun(environment: "dev" | "prod"): Promise<void> {
  const plan = asRecord(await runArtifactRegistryCommand([
    "deploy-service",
    "--env",
    environment,
    "--service",
    serviceId,
    "--commit",
    desiredCommit,
    "--dry-run",
  ]), `artifact dry-run ${environment}`);

  assertCondition(plan.ok === true, `${environment} dry-run must be ok`);
  assertCondition(plan.supported === true, `${environment} dry-run must be supported`);
  assertCondition(plan.dryRun === true, `${environment} dry-run must report dryRun=true`);
  assertCondition(plan.mutation === false, `${environment} dry-run must not mutate`);
  assertCondition(plan.serviceId === serviceId, `${environment} dry-run serviceId must be claudeqq`);
  assertCondition(plan.commit === desiredCommit, `${environment} dry-run commit must match desired commit`);
  assertCondition(plan.sourceRepo === sourceRepo, `${environment} dry-run source repo must match`);

  const source = asRecord(plan.source, `${environment}.source`);
  assertCondition(source.repo === sourceRepo, `${environment} dry-run source.repo must match`);
  assertCondition(source.commit === desiredCommit, `${environment} dry-run source.commit must match`);
  assertCondition(source.dockerfile === dockerfile, `${environment} dry-run source.dockerfile must match`);

  const build = asRecord(plan.build, `${environment}.build`);
  assertCondition(build.willCompile === false, `${environment} dry-run must not compile on the runtime target`);
  assertCondition(build.willRunDockerBuild === false, `${environment} dry-run must not docker build on the runtime target`);
  assertCondition(build.willRunDockerComposeBuild === false, `${environment} dry-run must not docker compose build on the runtime target`);

  const labels = asRecord(plan.requiredLabels, `${environment}.requiredLabels`);
  assertCondition(labels["unidesk.ai/service-id"] === serviceId, `${environment} labels must include service id`);
  assertCondition(labels["unidesk.ai/source-repo"] === sourceRepo, `${environment} labels must include source repo`);
  assertCondition(labels["unidesk.ai/source-commit"] === desiredCommit, `${environment} labels must include source commit`);
  assertCondition(labels["unidesk.ai/dockerfile"] === dockerfile, `${environment} labels must include dockerfile`);

  const target = asRecord(plan.target, `${environment}.target`);
  assertCondition(target.kind === "d601-k3s", `${environment} target kind must be d601-k3s`);
  assertCondition(target.namespace === (environment === "dev" ? "unidesk-dev" : "unidesk"), `${environment} namespace must match`);
  assertCondition(target.deployment === (environment === "dev" ? "claudeqq-dev" : "claudeqq"), `${environment} deployment must match`);
  assertCondition(target.service === (environment === "dev" ? "claudeqq-dev" : "claudeqq"), `${environment} service must match`);
  assertCondition(target.runtimeImage === `unidesk-claudeqq:${desiredCommit}`, `${environment} runtime image must be commit-pinned`);

  const validation = asArray(plan.validation, `${environment}.validation`).map((item, index) => stringField(item, `${environment}.validation[${index}]`));
  assertCondition(validation.some((line) => line.includes("service health via Kubernetes API service proxy")), `${environment} dry-run must require API service proxy health`);
  assertCondition(validation.some((line) => line.includes("source repo")), `${environment} dry-run must require source repo label validation`);
}

assertDeployJson();
assertArtifactCatalog();
assertAdapterSourceContract();
await assertDryRun("dev");
await assertDryRun("prod");

console.log(JSON.stringify({
  ok: true,
  serviceId,
  desiredCommit,
  eventPaths,
  checks: [
    "deploy.json desired dev/prod commit",
    "CI.json artifact producer contract",
    "adapter read-only event API fallback and health metadata",
    "dev/prod artifact-registry deploy-service dry-runs",
  ],
}, null, 2));
