import {
  RENDER_FIXTURES,
  UNKNOWN_HELPER_FIXTURES,
} from "@/internal/edm/edm-render.fixtures";
import { findUnknownBlockHelpers, renderEDM } from "@/internal/edm/edm-render";
import type { EDMTemplateObject } from "@/lib/storage/edm-storage";

/**
 * Run the EDM renderer's golden fixtures.
 *
 * A plain script rather than a vitest suite because apps/core has no test
 * framework or config today, and introducing one is a repo-wide convention
 * decision rather than something this feature should make on its own. The
 * fixtures are pure data, so porting to vitest later is a rename plus a wrapper.
 *
 *   tsx src/cmd/check-edm-render.ts            # exits non-zero on any failure
 *   tsx src/cmd/check-edm-render.ts --verbose  # print every case
 *
 * Green here means "the renderer still does what it did yesterday". It does NOT
 * mean "matches SendGrid" — see the header of edm-render.fixtures.ts.
 */

const VERBOSE = process.argv.includes("--verbose");

let passed = 0;
const failures: string[] = [];

const fail = (name: string, detail: string) => {
  failures.push(`${name}\n      ${detail.replace(/\n/g, "\n      ")}`);
};

for (const fixture of RENDER_FIXTURES) {
  const template: EDMTemplateObject = {
    subject: fixture.template.subject ?? "",
    html: fixture.template.html,
    plain: fixture.template.plain ?? "",
    generatePlain: fixture.template.generatePlain ?? false,
  };

  let rendered: { subject: string; html: string; plain: string } | null = null;
  let threw: Error | null = null;

  try {
    rendered = renderEDM(template, fixture.data);
  } catch (err) {
    threw = err as Error;
  }

  if (fixture.expectThrows) {
    if (threw) passed++;
    else fail(fixture.name, "expected a throw, got output");
    continue;
  }

  if (threw) {
    fail(fixture.name, `unexpected throw: ${threw.message}`);
    continue;
  }

  const problems: string[] = [];

  if (fixture.expectHtml !== undefined && rendered!.html !== fixture.expectHtml) {
    problems.push(
      `html mismatch\n  expected: ${JSON.stringify(fixture.expectHtml)}\n  actual:   ${JSON.stringify(rendered!.html)}`,
    );
  }
  if (fixture.expectSubject !== undefined && rendered!.subject !== fixture.expectSubject) {
    problems.push(
      `subject mismatch\n  expected: ${JSON.stringify(fixture.expectSubject)}\n  actual:   ${JSON.stringify(rendered!.subject)}`,
    );
  }
  for (const needle of fixture.contains ?? []) {
    if (!rendered!.html.includes(needle)) {
      problems.push(`missing expected substring: ${JSON.stringify(needle)}`);
    }
  }
  for (const needle of fixture.excludes ?? []) {
    if (rendered!.html.includes(needle)) {
      problems.push(`contains forbidden substring: ${JSON.stringify(needle)}`);
    }
  }

  if (problems.length) {
    fail(fixture.name, problems.join("\n"));
  } else {
    passed++;
    if (VERBOSE) console.log(`  ✓ ${fixture.name}`);
  }
}

for (const fixture of UNKNOWN_HELPER_FIXTURES) {
  const found = findUnknownBlockHelpers(fixture.html).map((h) => h.name).sort();
  const expected = [...fixture.expectNames].sort();

  const problems: string[] = [];
  if (JSON.stringify(found) !== JSON.stringify(expected)) {
    problems.push(
      `detector mismatch\n  expected: ${JSON.stringify(expected)}\n  actual:   ${JSON.stringify(found)}`,
    );
  }
  for (const absent of fixture.expectAbsent ?? []) {
    if (found.includes(absent)) problems.push(`should not have flagged: ${absent}`);
  }

  if (problems.length) {
    fail(`[detector] ${fixture.name}`, problems.join("\n"));
  } else {
    passed++;
    if (VERBOSE) console.log(`  ✓ [detector] ${fixture.name}`);
  }
}

const total = RENDER_FIXTURES.length + UNKNOWN_HELPER_FIXTURES.length;

console.log("");
if (failures.length === 0) {
  console.log(`  ${passed}/${total} EDM render fixtures passed.`);
  console.log("");
  console.log("  NOTE: this proves the renderer is self-consistent, NOT that it");
  console.log("  matches SendGrid. Every expectation is from documented behaviour.");
  console.log("  Real parity needs a live diff against SendGrid output.");
  process.exit(0);
} else {
  console.error(`  ${passed}/${total} passed, ${failures.length} FAILED:\n`);
  for (const f of failures) console.error(`    ✗ ${f}\n`);
  process.exit(1);
}
