#!/usr/bin/env node
/**
 * Syncs avatarDecorations.ts with what's actually available on the Nextcloud CDN.
 *
 * Usage:
 *   npm run sync:decorations
 *
 * Workflow after deleting files from Nextcloud:
 *   1. Delete decoration files from your Nextcloud share.
 *   2. Run: npm run sync:decorations
 *   3. It probes each catalog slug via HTTP HEAD and removes entries
 *      whose files returned 404. Empty categories are dropped automatically.
 *   4. Commit the updated avatarDecorations.ts.
 */

import { readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const catalogPath = join(root, 'src', 'app', 'features', 'lotus', 'avatarDecorations.ts');

// Single source of truth: the CDN base URL lives in avatarDecorations.ts as
// `export const DECORATION_CDN`. We extract it from there at runtime rather than
// re-declaring it here, so the build script and the app can never drift. This
// .mjs script can't cleanly import the browser-side .ts module (it's outside the
// Vite/TS app graph), so we parse the constant out of the file text instead.
// If you migrate the CDN, change it ONLY in avatarDecorations.ts.
const catalog = readFileSync(catalogPath, 'utf8');

const cdnMatch = catalog.match(/export const DECORATION_CDN\s*=\s*['"]([^'"]+)['"]/);
if (!cdnMatch) {
  console.error(
    'Could not find `export const DECORATION_CDN` in avatarDecorations.ts — ' +
      'the constant may have been renamed. Update scripts/syncDecorations.mjs.',
  );
  process.exit(1);
}
const CDN = cdnMatch[1];

// Extract all slugs from the catalog file
const slugMatches = [...catalog.matchAll(/slug: '([^']+)'/g)].map((m) => m[1]);

if (slugMatches.length === 0) {
  console.error('No slugs found in catalog — check the file path.');
  process.exit(1);
}

console.log(`Checking ${slugMatches.length} decorations against ${CDN} …`);
console.log('(This makes one HEAD request per decoration)\n');

// Probe all slugs in parallel batches of 16
async function headCheck(slug) {
  try {
    const res = await fetch(`${CDN}/${slug}.png`, { method: 'HEAD' });
    return { slug, ok: res.ok, status: res.status };
  } catch {
    // Network/DNS/TLS failure — NOT a confirmation the file is gone.
    return { slug, ok: false, status: 0, networkError: true };
  }
}

const BATCH = 16;
const results = [];
for (let i = 0; i < slugMatches.length; i += BATCH) {
  const batch = slugMatches.slice(i, i + BATCH);
  const batchResults = await Promise.all(batch.map(headCheck));
  results.push(...batchResults);
}

// Only a CONFIRMED HTTP 404 means the file is genuinely gone and safe to
// remove. A network error or any other non-ok status (5xx, 403, timeout) is
// ambiguous — the CDN may be unreachable — so refuse to remove anything and
// abort, otherwise a transient outage would wipe the whole catalog from source
// control (N119).
const transient = results.filter((r) => !r.ok && r.status !== 404);
if (transient.length > 0) {
  console.error(
    `Aborting: ${transient.length} decoration(s) returned a non-404 failure ` +
      `(network error / server error). The CDN may be unreachable — refusing to ` +
      `remove entries to avoid wiping the catalog.`,
  );
  transient
    .slice(0, 8)
    .forEach((r) =>
      console.error(`  ${r.slug}: ${r.networkError ? 'network error' : `HTTP ${r.status}`}`),
    );
  process.exit(1);
}

const missing = results.filter((r) => r.status === 404);
const found = results.filter((r) => r.ok);

if (missing.length === 0) {
  console.log(`All ${found.length} decorations are available — catalog is up to date.`);
  process.exit(0);
}

console.log(`Found: ${found.length}  Missing: ${missing.length}\n`);
missing.forEach((r) => console.log(`  Removing (HTTP ${r.status}): ${r.slug}`));

const missingSet = new Set(missing.map((r) => r.slug));

// Remove individual entries for missing slugs
let updated = catalog.replace(/^[ \t]*\{ slug: '([^']+)', name: .+\},?\r?\n/gm, (match, slug) =>
  missingSet.has(slug) ? '' : match,
);

// Drop category blocks that now have an empty decorations array
updated = updated.replace(
  /  \{\n    id: '[^']+',\n    label: '[^']+',\n    decorations: \[\n?[ \t]*\],?\n  \},?\n/g,
  '',
);

// Clean up stray blank lines
updated = updated.replace(/\n{3,}/g, '\n\n');

writeFileSync(catalogPath, updated, 'utf8');
console.log(
  `\nDone. Removed ${missing.length} entr${missing.length === 1 ? 'y' : 'ies'} from the catalog.`,
);
console.log('Review with: git diff src/app/features/lotus/avatarDecorations.ts');
