#!/usr/bin/env node
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

import { ensureNotRepoReportsPath } from "./src/report-paths.mjs";

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

function assertObject(value, label) {
  assert.ok(value && typeof value === "object" && !Array.isArray(value), `${label} must be an object`);
}

function assertString(value, label) {
  assert.equal(typeof value, "string", `${label} must be a string`);
  assert.ok(value.trim().length > 0, `${label} must not be empty`);
}

function assertBoolean(value, label) {
  assert.equal(typeof value, "boolean", `${label} must be a boolean`);
}

function repoRelative(inputPath) {
  const absolute = path.isAbsolute(inputPath) ? inputPath : path.resolve(process.cwd(), inputPath);
  return path.relative(repoRoot, absolute).split(path.sep).join("/");
}

async function readJson(inputPath) {
  ensureNotRepoReportsPath(repoRoot, inputPath, "validation input");
  const absolute = path.isAbsolute(inputPath) ? inputPath : path.resolve(process.cwd(), inputPath);
  const raw = await readFile(absolute, "utf8");
  return {
    path: absolute,
    relativePath: repoRelative(absolute),
    report: JSON.parse(raw)
  };
}

function validateReportShape(report, label) {
  assertObject(report, label);
  assertString(report.reportKind ?? report.taskId ?? report.kind ?? "temporary-json", `${label}.kind`);
  if (Object.hasOwn(report, "devOnly")) assertBoolean(report.devOnly, `${label}.devOnly`);
  if (Object.hasOwn(report, "prodDisabled")) assertBoolean(report.prodDisabled, `${label}.prodDisabled`);
  if (Array.isArray(report.blockers)) {
    for (const [index, blocker] of report.blockers.entries()) {
      assertObject(blocker, `${label}.blockers[${index}]`);
      assertString(blocker.scope ?? blocker.code ?? blocker.type ?? "blocker", `${label}.blockers[${index}].scope`);
    }
  }
  if (Array.isArray(report.validationCommands)) {
    for (const [index, command] of report.validationCommands.entries()) {
      assertString(command, `${label}.validationCommands[${index}]`);
    }
  }
  if (Object.hasOwn(report, "dryRunPlan")) {
    assertObject(report.dryRunPlan, `${label}.dryRunPlan`);
    if (Object.hasOwn(report.dryRunPlan, "dryRunCallsLiveEndpoints")) {
      assert.equal(report.dryRunPlan.dryRunCallsLiveEndpoints, false, `${label}.dryRunPlan.dryRunCallsLiveEndpoints`);
    }
    if (Object.hasOwn(report.dryRunPlan, "liveWriteWillRun")) {
      assert.equal(report.dryRunPlan.liveWriteWillRun, false, `${label}.dryRunPlan.liveWriteWillRun`);
    }
    if (Object.hasOwn(report.dryRunPlan, "evidenceLevel")) {
      assert.equal(report.dryRunPlan.evidenceLevel, "DRY-RUN", `${label}.dryRunPlan.evidenceLevel`);
    }
    assertObject(report.liveOperation, `${label}.liveOperation`);
    assert.notEqual(
      report.liveOperation.status,
      "pass",
      `${label}.liveOperation.status cannot be pass when only a DRY-RUN plan is present`
    );
    for (const field of ["operationId", "traceId", "auditId", "evidenceId"]) {
      if (Object.hasOwn(report.liveOperation, field)) {
        assert.equal(report.liveOperation[field], "not_observed", `${label}.liveOperation.${field} must remain not_observed for DRY-RUN evidence`);
      }
    }
  }
}

async function main(argv) {
  if (argv.length === 0) {
    process.stdout.write(`${JSON.stringify({
      ok: true,
      status: "skipped",
      reason: "repository report files are disabled; pass explicit temporary JSON paths to validate them"
    })}\n`);
    return;
  }

  const results = [];
  for (const inputPath of argv) {
    const { relativePath, report } = await readJson(inputPath);
    validateReportShape(report, relativePath);
    results.push(relativePath);
  }

  process.stdout.write(`${JSON.stringify({
    ok: true,
    status: "pass",
    validated: results.length,
    files: results
  })}\n`);
}

try {
  await main(process.argv.slice(2));
} catch (error) {
  process.stderr.write(`${JSON.stringify({
    ok: false,
    status: "blocked",
    error: error instanceof Error ? error.message : String(error)
  })}\n`);
  process.exitCode = 1;
}
