/**
 * variant/gallery.mjs — render the side-by-side picker HTML (C1, Wave 2).
 *
 * Pure function.  Takes a list of variant entries and returns the gallery
 * HTML string.  The CLI layer writes it to disk; this module has no I/O
 * so tests can diff the output directly.
 *
 * Design constraints (from overview.md §10.2):
 *   - Zero runtime deps.  Everything inlined.  Tailwind loaded from the
 *     CDN only.  The gallery must open via `file://` with no dev server.
 *   - No JS event handlers that try to write back to disk (file:// has
 *     no privilege for that).  Picking happens by running a terminal
 *     command — the gallery's job is to make the letter obvious and the
 *     command copy-pasteable.
 *   - Responsive grid: 1 column on narrow, 2 on medium, N on wide.
 *   - Each variant iframe is rendered at full device-width-feel, not a
 *     thumbnail, so the user can actually read the content.
 */

/**
 * @typedef {{ letter: string, filename: string, style?: string, description?: string }} GalleryVariant
 * @typedef {{ feature: string, iteration: string, variants: GalleryVariant[], taskDir: string }} GalleryInput
 */

// "N" colors come from the DESIGN.md scaffold palette so the gallery
// chrome matches the tokens the variants are rendering against.  Keep
// tailwind utilities — no custom CSS beyond the frame layout.
const GALLERY_BG = 'bg-slate-950'
const GALLERY_TEXT = 'text-slate-100'
const ACCENT = 'text-indigo-400'

/**
 * Escape user-supplied strings that land inside HTML attribute values or
 * text nodes.  This is the only place raw input touches the rendered
 * document, so it's the only place we escape.
 */
function escapeHtml(s) {
	if (typeof s !== 'string') return ''
	return s
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;')
		.replace(/'/g, '&#39;')
}

/**
 * Build the gallery HTML.
 *
 * @param {GalleryInput} input
 * @returns {string}
 */
export function renderGallery(input) {
	const { feature, iteration, variants, taskDir } = input
	if (!Array.isArray(variants) || variants.length === 0) {
		throw new Error('renderGallery: variants must be a non-empty array')
	}

	// grid-cols scales with variant count, capped at 4 columns on ultrawide
	// (even N=5 runs get the 5th variant onto a second row for readability).
	const cols = Math.min(variants.length, 4)
	const gridCols = `xl:grid-cols-${cols}`

	const cards = variants
		.map((v) => {
			const letter = escapeHtml(String(v.letter).toUpperCase())
			const style = escapeHtml(v.style ?? '')
			const description = escapeHtml(v.description ?? '')
			const filename = escapeHtml(v.filename)

			// Each iframe uses a relative src so the gallery works from
			// anywhere (the browser resolves against the gallery file's URL).
			return [
				'      <article class="group overflow-hidden rounded-xl border border-slate-800 bg-slate-900 shadow-xl">',
				'        <header class="flex items-center justify-between gap-3 border-b border-slate-800 px-4 py-3">',
				'          <div class="flex items-center gap-3">',
				`            <span class="inline-flex h-9 w-9 items-center justify-center rounded-md bg-indigo-500/10 text-lg font-bold ${ACCENT}">${letter}</span>`,
				'            <div class="min-w-0">',
				`              <div class="truncate text-sm font-semibold text-slate-100">${style || 'Variant'}</div>`,
				`              <div class="truncate text-xs text-slate-400">${description}</div>`,
				'            </div>',
				'          </div>',
				`          <code class="shrink-0 rounded bg-slate-800 px-2 py-1 text-xs text-slate-300">${filename}</code>`,
				'        </header>',
				`        <iframe src="${filename}" title="Variant ${letter}" loading="lazy" class="block h-[720px] w-full bg-white"></iframe>`,
				'        <footer class="flex items-center justify-between gap-3 border-t border-slate-800 px-4 py-3">',
				`          <div class="text-xs text-slate-500">Pick this one:</div>`,
				`          <code class="rounded bg-slate-800 px-2 py-1 text-xs text-slate-100">/apt:mockup --pick ${letter.toLowerCase()}</code>`,
				'        </footer>',
				'      </article>',
			].join('\n')
		})
		.join('\n')

	const featureEsc = escapeHtml(feature)
	const iterationEsc = escapeHtml(iteration)
	const taskDirEsc = escapeHtml(taskDir)

	return [
		'<!doctype html>',
		'<html lang="en">',
		'  <head>',
		'    <meta charset="utf-8" />',
		'    <meta name="viewport" content="width=device-width, initial-scale=1" />',
		`    <title>Variant gallery — ${featureEsc} (iteration ${iterationEsc})</title>`,
		'    <script src="https://cdn.tailwindcss.com"></script>',
		`    <!-- iteration: ${iterationEsc}  task_dir: ${taskDirEsc} -->`,
		'  </head>',
		`  <body class="${GALLERY_BG} ${GALLERY_TEXT} antialiased">`,
		'    <main class="mx-auto max-w-[1600px] space-y-6 p-6 lg:p-8">',
		'      <header class="flex flex-wrap items-end justify-between gap-4">',
		'        <div>',
		`          <div class="text-xs uppercase tracking-[0.2em] text-slate-500">Variant gallery · iteration ${iterationEsc}</div>`,
		`          <h1 class="mt-1 text-2xl font-semibold text-slate-100">${featureEsc}</h1>`,
		`          <p class="mt-1 text-sm text-slate-400">${variants.length} parallel generations. Open this page via <code>file://</code>, scan the iframes, pick the letter that lands.</p>`,
		'        </div>',
		'        <div class="flex flex-col gap-2 text-xs text-slate-400 sm:items-end">',
		`          <div>Directory: <code class="text-slate-300">${taskDirEsc}/mockups/</code></div>`,
		'          <div>Pick: run <code class="rounded bg-slate-800 px-2 py-1 text-slate-100">/apt:mockup --pick &lt;letter&gt;</code> in your terminal.</div>',
		'        </div>',
		'      </header>',
		`      <div class="grid grid-cols-1 gap-4 md:grid-cols-2 ${gridCols}">`,
		cards,
		'      </div>',
		'      <footer class="pt-6 text-center text-xs text-slate-500">',
		`        Generated by <code>apt-tools variant-gallery</code> · iteration ${iterationEsc} · ${new Date().toISOString()}`,
		'      </footer>',
		'    </main>',
		'  </body>',
		'</html>',
		'',
	].join('\n')
}

const LETTER_ORDER = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

/**
 * Turn a list of variant filenames (e.g.
 * `iteration-001-variant-a.html`) into the structured GalleryVariant[]
 * that renderGallery expects.  Filenames missing the `-variant-<letter>`
 * segment are skipped; caller can surface a warning.
 *
 * @param {string[]} filenames — sibling filenames under mockups/
 * @param {Record<string, { style?: string, description?: string } | undefined>} [styleByLetter]
 * @returns {{ variants: GalleryVariant[], skipped: string[] }}
 */
export function variantEntriesFromFilenames(filenames, styleByLetter = {}) {
	const re = /iteration-(\d{3})-variant-([a-z])\.html$/i
	const seen = new Map()
	const skipped = []
	for (const raw of filenames) {
		const m = raw.match(re)
		if (!m) {
			skipped.push(raw)
			continue
		}
		const letter = m[2].toLowerCase()
		const meta = styleByLetter[letter] ?? {}
		seen.set(letter, {
			letter,
			filename: raw,
			style: meta.style,
			description: meta.description,
		})
	}
	const variants = LETTER_ORDER.filter((l) => seen.has(l)).map(
		(l) => /** @type {GalleryVariant} */ (seen.get(l)),
	)
	return { variants, skipped }
}
