#!/usr/bin/env node
// check-site.mjs — runs on Windows, macOS, and Linux.
// Fails (exit code 1) if any page breaks a rule. Run it before you publish.
//   node check-site.mjs

import { readdirSync, statSync, readFileSync } from "fs";
import { join, extname } from "path";

const MAX_BYTES = 500 * 1024; // 500KB
const MEDIA = [".png", ".jpg", ".jpeg", ".webp", ".avif", ".svg", ".gif", ".mp4", ".webm"];
const problems = [];

// Walk every folder, not just this one.
function walk(dir) {
  let out = [];
  for (const name of readdirSync(dir)) {
    if (name === "node_modules" || name.startsWith(".")) continue;
    const full = join(dir, name);
    out = statSync(full).isDirectory() ? out.concat(walk(full)) : out.concat(full);
  }
  return out;
}

// Check only the folders that actually get published.
// Pass folders as arguments, or it uses site/ and assets/ when they exist.
const args = process.argv.slice(2);
const roots = args.length ? args
  : ["site", "assets"].filter((d) => { try { return statSync(d).isDirectory(); } catch { return false; } });
const files = (roots.length ? roots : ["."]).flatMap(walk);

// RULE 1: every picture needs a description (alt), even if the tag spans lines.
for (const f of files.filter((f) => extname(f) === ".html")) {
  const html = readFileSync(f, "utf8");
  for (const tag of html.match(/<img\b[\s\S]*?>/gi) || []) {
    if (!/\balt\s*=/i.test(tag)) {
      problems.push(`MISSING DESCRIPTION  ${f}  ${tag.replace(/\s+/g, " ").slice(0, 60)}`);
    }
  }
}

// RULE 2: nothing over 500KB, anywhere in the project.
for (const f of files.filter((f) => MEDIA.includes(extname(f).toLowerCase()))) {
  const kb = Math.round(statSync(f).size / 1024);
  if (statSync(f).size > MAX_BYTES) problems.push(`TOO BIG  ${f}  ${kb}KB`);
}

// RULE 3: every page carries the same menu.
const pages = files.filter((f) => extname(f) === ".html");
const menus = new Map();
for (const f of pages) {
  const m = readFileSync(f, "utf8").match(/<nav\b[\s\S]*?<\/nav>/i);
  if (m) menus.set(f, m[0].replace(/\s+/g, " ").trim());
}
const distinct = new Set(menus.values());
if (distinct.size > 1) {
  problems.push(`MENUS DIFFER  ${[...menus.keys()].join(", ")}`);
}

// RULE 4: the findability basics must be present on the home page.
const home = files.find((f) => f.endsWith("index.html"));
if (home) {
  const html = readFileSync(home, "utf8");
  const title = (html.match(/<title>([\s\S]*?)<\/title>/i) || [])[1] || "";
  if (title.trim().length < 15 || /^home$/i.test(title.trim())) {
    problems.push(`WEAK TITLE  ${home}  "${title.trim()}"`);
  }
  for (const tag of ['name="description"', 'property="og:title"', 'property="og:image"']) {
    if (!html.includes(tag)) problems.push(`MISSING TAG  ${home}  ${tag}`);
  }
  // RULE 5: the last beat must actually do something.
  if (!/href="(https:\/\/wa\.me\/|mailto:|tel:|\/api\/)/.test(html)) {
    problems.push(`DEAD LAST BEAT  ${home}  no working contact link found`);
  }
}

// RULE 6: the sharing card must exist and be the right shape.
const og = files.find((f) => f.endsWith("og.png"));
if (!og) problems.push("MISSING SHARING CARD  expected site/og.png (1200x630)");

if (problems.length) {
  console.error(problems.join("\n"));
  console.error(`\n${problems.length} problem(s). Fix these before publishing.`);
  process.exit(1);
}
console.log("All checks passed.");
