<script lang="ts">
	// Typed by what is drawn rather than by `Book`: the duplicates review holds
	// candidates that carry a title and author names without a whole record, and a
	// `Book` satisfies this shape anyway.
	let {
		book,
		class: className = ''
	}: {
		book: { title: string; authors?: { name: string }[] | null };
		class?: string;
	} = $props();

	/**
	 * Bookcloth tones rather than a point on the hue wheel.
	 *
	 * The old placeholder derived a hue from the title with `hash % 360`, which
	 * eventually lands on acid green. Hashing into a fixed list keeps every
	 * generated cover looking like it came from the same library.
	 */
	const CLOTH = [
		'#1f5f5b', // teal, the app's own accent
		'#7a2e2a', // oxblood
		'#2f4858', // slate
		'#3c5148', // forest
		'#6b4a6e', // plum
		'#8a6a1f', // brass
		'#2b3038', // graphite
		'#5a4632' // tobacco
	];

	/** Weaves, as CSS gradients — an SVG <pattern> would need a unique id per cover. */
	const WEAVE = [
		// twill
		`repeating-linear-gradient(45deg, transparent 0 4px, currentColor 4px 5px)`,
		// horizontal ribbing
		`repeating-linear-gradient(0deg, transparent 0 5px, currentColor 5px 6px)`,
		// crosshatch
		`repeating-linear-gradient(45deg, transparent 0 6px, currentColor 6px 7px),
		 repeating-linear-gradient(-45deg, transparent 0 6px, currentColor 6px 7px)`,
		// vertical laid lines
		`repeating-linear-gradient(90deg, transparent 0 5px, currentColor 5px 6px)`
	];

	/** Stable per title, so a book always looks the same. */
	function hash(value: string) {
		let out = 7;
		for (const char of value) out = (out * 31 + char.charCodeAt(0)) % 100_000;
		return out;
	}

	const seed = $derived(hash(book.title || 'Untitled'));
	const cloth = $derived(CLOTH[seed % CLOTH.length]);
	const weave = $derived(WEAVE[(seed >> 3) % WEAVE.length]);

	const authors = $derived(book.authors?.map((author) => author.name).join(', ') ?? '');

	/**
	 * Stepped by length rather than scaled continuously: a long title shrunk to
	 * fit would end up smaller than the author line, which reads as a mistake.
	 * Units are cqw so the whole thing works at any size the caller asks for.
	 */
	const titleSize = $derived.by(() => {
		const length = (book.title ?? '').length;
		if (length <= 14) return 17;
		if (length <= 28) return 13;
		if (length <= 46) return 10;
		return 8.5;
	});
</script>

<!--
	Drawn, not stored. It costs nothing, follows the title when it is edited, and
	is replaced the moment a real cover is uploaded. Colours are fixed rather than
	themed: this is an object on a shelf beside real artwork, which does not
	change with the theme either.
-->
<div
	class="generated-cover {className}"
	style="--cloth: {cloth};"
	role="img"
	aria-label="Generated cover for {book.title}"
>
	<div class="weave" style="background-image: {weave};"></div>

	<div class="type">
		<span class="title" style="font-size: {titleSize}cqw;">{book.title}</span>
		{#if authors}
			<span class="author">{authors}</span>
		{/if}
	</div>
</div>

<style>
	.generated-cover {
		container-type: inline-size;
		position: relative;
		overflow: hidden;
		height: 100%;
		width: 100%;
		background: var(--cloth);
		color: #f4f1ec;
		isolation: isolate;
	}

	/* currentColor drives the gradient, so one rule covers every weave. */
	.weave {
		position: absolute;
		inset: 0;
		color: #ffffff;
		opacity: 0.14;
		z-index: -1;
	}

	/* A little depth so it does not read as a flat swatch next to a photograph. */
	.generated-cover::after {
		content: '';
		position: absolute;
		inset: 0;
		background: linear-gradient(150deg, rgb(255 255 255 / 0.1), rgb(0 0 0 / 0.28));
		z-index: -1;
	}

	.type {
		display: flex;
		height: 100%;
		flex-direction: column;
		justify-content: space-between;
		gap: 6cqw;
		padding: 10cqw 9cqw;
	}

	.title {
		font-family: var(--app-font-serif, Georgia, serif);
		line-height: 1.04;
		letter-spacing: -0.02em;
		text-wrap: balance;
		/* Clip rather than spill: an overflowing title looks broken, a clipped one
		   just looks like a long title. */
		display: -webkit-box;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: 6;
		line-clamp: 6;
		overflow: hidden;
	}

	.author {
		font-family: var(--app-font-mono, ui-monospace, monospace);
		font-size: 4.4cqw;
		letter-spacing: 0.12em;
		text-transform: uppercase;
		opacity: 0.75;
		display: -webkit-box;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: 2;
		line-clamp: 2;
		overflow: hidden;
	}

	/* Below about a centimetre the author is unreadable, and the padding is
	   eating the title. Show the title alone. */
	@container (max-width: 72px) {
		.author {
			display: none;
		}

		.type {
			padding: 8cqw;
		}
	}
</style>
