import * as fs from "node:fs";
import * as path from "node:path";
import { generateImage } from "../src/api/gemini";
import { loadConfigFromObject } from "../src/api/config";
import type { GeminiConfig } from "../src/api/config";

// Visual descriptions per companion (from SPEC.md section 9.2)
const companionDefs = [
	{
		type: "fee-lila",
		name: "Fee Lila",
		visualDescription:
			"a small fairy with lavender-purple wings, a flowing lilac dress, big warm brown eyes, a tiny flower crown made of forget-me-nots, light skin, and short wavy auburn hair",
	},
	{
		type: "einhorn-stella",
		name: "Einhorn Stella",
		visualDescription:
			"a small unicorn with a white coat, a shimmering golden horn, a flowing mane in soft rainbow pastels (pink, lavender, light blue), big dark eyes with long lashes, and small silver hooves",
	},
	{
		type: "fuchs-finn",
		name: "Fuchs Finn",
		visualDescription:
			"a young red fox with warm orange-brown fur, a white chest and belly, a bushy tail with a white tip, bright curious amber eyes, and slightly oversized pointed ears",
	},
	{
		type: "eule-elsa",
		name: "Eule Elsa",
		visualDescription:
			"a small round owl with soft brown-and-cream feathers, a heart-shaped face, large round golden eyes, tiny ear tufts, and small talons perched on a mossy branch",
	},
];

function sleep(ms: number): Promise<void> {
	return new Promise((resolve) => setTimeout(resolve, ms));
}

async function saveBlobToFile(blob: Blob, filePath: string): Promise<void> {
	const buffer = Buffer.from(await blob.arrayBuffer());
	const dir = path.dirname(filePath);
	if (!fs.existsSync(dir)) {
		fs.mkdirSync(dir, { recursive: true });
	}
	fs.writeFileSync(filePath, buffer);
}

async function main(): Promise<void> {
	// Load config from public/config.json
	const configPath = path.resolve("public/config.json");
	if (!fs.existsSync(configPath)) {
		console.error("ERROR: public/config.json not found");
		process.exit(1);
	}

	const configData = JSON.parse(
		fs.readFileSync(configPath, "utf-8"),
	) as GeminiConfig;

	if (!configData.geminiApiKey || !configData.imageModel) {
		console.error("ERROR: config.json missing geminiApiKey or imageModel");
		process.exit(1);
	}

	loadConfigFromObject(configData);

	let failures = 0;

	// Generate character sheets and avatars for each companion
	for (const companion of companionDefs) {
		const charSheetPath = path.resolve(
			`src/assets/companions/${companion.type}/character-sheet.png`,
		);
		const avatarPath = path.resolve(
			`src/assets/companions/${companion.type}/avatar.png`,
		);

		// Skip if both files already exist and are non-empty
		if (
			fs.existsSync(charSheetPath) &&
			fs.statSync(charSheetPath).size > 1024 &&
			fs.existsSync(avatarPath) &&
			fs.statSync(avatarPath).size > 1024
		) {
			console.log(
				`Skipping ${companion.name} — character-sheet.png and avatar.png already exist`,
			);
			continue;
		}

		// Generate character sheet
		console.log(`Generating character sheet for ${companion.name}...`);
		const charSheetPrompt = `Character sheet for a children's book. The character is ${companion.name}, ${companion.visualDescription}.
Show the character in 3 views: front view, side view, and a close-up of the face.
All views must show the EXACT SAME character with identical colors, proportions, and features.
Art style: Watercolor children's book illustration, warm pastel colors, soft edges, magical forest theme. White background.
Label each view clearly: "FRONT" "SIDE" "FACE"
The character must be: cute, friendly, approachable, with large expressive eyes. Suitable for a 7-year-old audience.`;

		try {
			const charSheetBlob = await generateImage(
				charSheetPrompt,
				[],
				configData,
			);
			if (charSheetBlob) {
				await saveBlobToFile(charSheetBlob, charSheetPath);
				console.log(
					`  Saved character sheet: ${charSheetPath} (${fs.statSync(charSheetPath).size} bytes)`,
				);
			} else {
				console.error(
					`  ERROR: Failed to generate character sheet for ${companion.name}`,
				);
				failures++;
				continue; // Skip avatar if character sheet failed (needed as reference)
			}
		} catch (err) {
			console.error(
				`  ERROR generating character sheet for ${companion.name}:`,
				err,
			);
			failures++;
			continue;
		}

		await sleep(2000); // Rate limit delay

		// Generate avatar using character sheet as reference
		console.log(`Generating avatar for ${companion.name}...`);
		const avatarPrompt = `Create a single portrait of this exact character from the reference sheet.
Show only the face and upper body, looking directly at the viewer with a warm, friendly expression.
Same art style as the reference. Square format. No text. No background (or simple soft gradient).`;

		try {
			// Read the character sheet back as a reference image
			const charSheetBuffer = fs.readFileSync(charSheetPath);
			const charSheetRef = new Blob([charSheetBuffer], {
				type: "image/png",
			});

			const avatarBlob = await generateImage(
				avatarPrompt,
				[charSheetRef],
				configData,
			);
			if (avatarBlob) {
				await saveBlobToFile(avatarBlob, avatarPath);
				console.log(
					`  Saved avatar: ${avatarPath} (${fs.statSync(avatarPath).size} bytes)`,
				);
			} else {
				console.error(
					`  ERROR: Failed to generate avatar for ${companion.name}`,
				);
				failures++;
			}
		} catch (err) {
			console.error(
				`  ERROR generating avatar for ${companion.name}:`,
				err,
			);
			failures++;
		}

		await sleep(2000); // Rate limit delay
	}

	// Generate style reference (initial forest scene)
	const styleRefPath = path.resolve(
		"src/assets/style-reference/initial-forest-scene.png",
	);

	if (
		fs.existsSync(styleRefPath) &&
		fs.statSync(styleRefPath).size > 1024
	) {
		console.log(
			"Skipping style reference — initial-forest-scene.png already exists",
		);
	} else {
		console.log("Generating style reference (initial forest scene)...");
		const styleRefPrompt = `Watercolor children's book illustration of a magical forest clearing.
Warm pastel colors, soft edges, dreamy atmosphere. A gentle meadow surrounded
by friendly trees, dappled sunlight, small flowers, and a hint of magic sparkles.
No text, no letters, no characters. This sets the visual style for all future forest images.`;

		try {
			const styleRefBlob = await generateImage(
				styleRefPrompt,
				[],
				configData,
			);
			if (styleRefBlob) {
				await saveBlobToFile(styleRefBlob, styleRefPath);
				console.log(
					`  Saved style reference: ${styleRefPath} (${fs.statSync(styleRefPath).size} bytes)`,
				);
			} else {
				console.error("  ERROR: Failed to generate style reference");
				failures++;
			}
		} catch (err) {
			console.error("  ERROR generating style reference:", err);
			failures++;
		}
	}

	// Summary
	console.log("\n--- Generation Summary ---");
	const pngFiles = [
		"src/assets/companions/fee-lila/character-sheet.png",
		"src/assets/companions/fee-lila/avatar.png",
		"src/assets/companions/einhorn-stella/character-sheet.png",
		"src/assets/companions/einhorn-stella/avatar.png",
		"src/assets/companions/fuchs-finn/character-sheet.png",
		"src/assets/companions/fuchs-finn/avatar.png",
		"src/assets/companions/eule-elsa/character-sheet.png",
		"src/assets/companions/eule-elsa/avatar.png",
		"src/assets/style-reference/initial-forest-scene.png",
	];

	let existing = 0;
	for (const f of pngFiles) {
		const resolved = path.resolve(f);
		if (fs.existsSync(resolved) && fs.statSync(resolved).size > 1024) {
			existing++;
			console.log(`  OK: ${f}`);
		} else {
			console.log(`  MISSING: ${f}`);
		}
	}

	console.log(`\n${existing}/9 assets generated successfully.`);
	if (failures > 0) {
		console.log(`${failures} generation(s) failed.`);
	}
	process.exit(failures > 0 ? 1 : 0);
}

main();
