#!/usr/bin/env node

const fs = require("node:fs");
const path = require("node:path");

const METADATA_URL = "https://fonts.google.com/metadata/fonts";

function toExportName(family) {
  return family
    .replace(/[^0-9A-Za-z]+/g, "_")
    .replace(/^_+|_+$/g, "")
    .replace(/_+/g, "_");
}

function assertValidExports(names) {
  const invalid = names.filter((name) => !/^[A-Za-z_$][0-9A-Za-z_$]*$/.test(name));
  if (invalid.length > 0) {
    throw new Error(`Invalid export identifiers: ${invalid.slice(0, 10).join(", ")}`);
  }
  const seen = new Set();
  const dupes = new Set();
  for (const name of names) {
    if (seen.has(name)) dupes.add(name);
    seen.add(name);
  }
  if (dupes.size > 0) {
    throw new Error(`Duplicate export identifiers: ${Array.from(dupes).slice(0, 10).join(", ")}`);
  }
}

function stripXssiPrefix(text) {
  if (text.startsWith(")]}'")) {
    return text.replace(/^\)\]\}'\n?/, "");
  }
  return text;
}

async function loadMetadata(metadataPath) {
  if (metadataPath) {
    const text = fs.readFileSync(metadataPath, "utf-8");
    return JSON.parse(stripXssiPrefix(text));
  }
  const res = await fetch(METADATA_URL);
  if (!res.ok) {
    throw new Error(`Failed to fetch metadata: ${res.status} ${res.statusText}`);
  }
  const text = await res.text();
  return JSON.parse(stripXssiPrefix(text));
}

function writeFixture(families) {
  const fixturePath = path.join(process.cwd(), "tests/fixtures/google-fonts.json");
  const data = {
    source: METADATA_URL,
    generatedAt: new Date().toISOString().slice(0, 10),
    count: families.length,
    families,
  };
  fs.mkdirSync(path.dirname(fixturePath), { recursive: true });
  fs.writeFileSync(fixturePath, JSON.stringify(data, null, 2) + "\n");
}

function writeGeneratedTs(entries) {
  const outPath = path.join(process.cwd(), "packages/vinext/src/shims/font-google.generated.ts");
  const lines = [];
  lines.push("// Generated by scripts/generate-google-fonts.js");
  lines.push(`// Source: ${METADATA_URL}`);
  lines.push("// @generated");
  lines.push('import { createFontLoader, type FontLoader } from "./font-google-base";');
  for (const { exportName, family } of entries) {
    lines.push(
      `export const ${exportName}: FontLoader = /*#__PURE__*/ createFontLoader(${JSON.stringify(family)});`,
    );
  }
  lines.push("");
  fs.writeFileSync(outPath, lines.join("\n"));
}

function writeGeneratedDts(exportNames) {
  const outPath = path.join(
    process.cwd(),
    "packages/vinext/src/shims/next-shims-font-google.generated.d.ts",
  );
  const lines = [];
  lines.push("// Generated by scripts/generate-google-fonts.js");
  lines.push(`// Source: ${METADATA_URL}`);
  lines.push("// @generated");
  lines.push('declare module "next/font/google" {');
  for (const name of exportNames) {
    lines.push(`  export const ${name}: FontLoader;`);
  }
  lines.push("}");
  lines.push("");
  fs.writeFileSync(outPath, lines.join("\n"));
}

async function main() {
  const args = process.argv.slice(2);
  let metadataPath;
  for (let i = 0; i < args.length; i += 1) {
    if (args[i] === "--metadata-path") {
      metadataPath = args[i + 1];
      i += 1;
    }
  }
  if (!metadataPath && process.env.GOOGLE_FONTS_METADATA_PATH) {
    metadataPath = process.env.GOOGLE_FONTS_METADATA_PATH;
  }

  const metadata = await loadMetadata(metadataPath);
  const families = metadata.familyMetadataList?.map((f) => f.family) ?? [];
  if (families.length === 0) {
    throw new Error("No font families found in metadata");
  }

  const entries = families.map((family) => ({
    family,
    exportName: toExportName(family),
  }));
  entries.sort((a, b) => a.exportName.localeCompare(b.exportName));

  const exportNames = entries.map((e) => e.exportName);
  assertValidExports(exportNames);

  writeFixture(families);
  writeGeneratedTs(entries);
  writeGeneratedDts(exportNames);

  console.log(`Generated ${entries.length} fonts`);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
