/**
 * Mockup compositor — generates product mockups using Sharp.
 *
 * Supports shirt, sticker, poster templates with design overlay.
 * Falls back to local compositing when Printful API is unavailable.
 */

import sharp from "sharp";
import { PrintfulClient } from "./pod/printful";
import type { PodMockup } from "./pod/types";

// ── Template definitions ──

interface MockupTemplate {
	/** Width/height of the final mockup image */
	width: number;
	height: number;
	/** Where the design is placed: [x, y, w, h] */
	designBox: [number, number, number, number];
	/** Blend mode for compositing */
	blend: "over" | "screen";
	/** Background color (used when no template image) */
	bgColor: string;
}

const TEMPLATES: Record<string, MockupTemplate> = {
	shirt: {
		width: 1024,
		height: 1024,
		designBox: [262, 230, 500, 450],
		blend: "screen",
		bgColor: "#1a1a1a",
	},
	sticker: {
		width: 1024,
		height: 1024,
		designBox: [270, 210, 470, 530],
		blend: "over",
		bgColor: "#1e293b",
	},
	print: {
		width: 1024,
		height: 1024,
		designBox: [225, 225, 575, 500],
		blend: "over",
		bgColor: "#f8fafc",
	},
	poster: {
		width: 1024,
		height: 1024,
		designBox: [225, 225, 575, 500],
		blend: "over",
		bgColor: "#f8fafc",
	},
};

// ── Mockup cache ──

const mockupCache = new Map<string, Buffer>();
const MAX_CACHE = 100;

function evictCache() {
	if (mockupCache.size <= MAX_CACHE) return;
	const iter = mockupCache.keys();
	while (mockupCache.size > MAX_CACHE) {
		const next = iter.next();
		if (next.done) break;
		mockupCache.delete(next.value);
	}
}

// ── Local compositing (Sharp-based fallback) ──

/**
 * Generate a product mockup by compositing the design onto a generated template.
 * Uses SVG-based template backgrounds for a clean look.
 */
async function localComposite(
	designBuffer: Buffer,
	type: string,
): Promise<Buffer> {
	const template = TEMPLATES[type] || TEMPLATES.shirt;
	const [dx, dy, dw, dh] = template.designBox;

	// Resize design to fit the design box
	const resizedDesign = await sharp(designBuffer)
		.resize(dw, dh, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } })
		.png()
		.toBuffer();

	// Create the base mockup image
	let base: sharp.Sharp;

	if (type === "shirt") {
		// Generate a shirt silhouette SVG
		base = sharp(Buffer.from(generateShirtSvg(template.width, template.height)))
			.resize(template.width, template.height);
	} else if (type === "sticker") {
		base = sharp(Buffer.from(generateStickerSvg(template.width, template.height)))
			.resize(template.width, template.height);
	} else {
		// Poster/print: simple background
		base = sharp({
			create: {
				width: template.width,
				height: template.height,
				channels: 4,
				background: template.bgColor,
			},
		}).png();
	}

	// Composite design onto template
	const result = await base
		.composite([
			{
				input: resizedDesign,
				top: dy,
				left: dx,
				blend: template.blend === "screen" ? "screen" : "over",
			},
		])
		.png()
		.toBuffer();

	return result;
}

function generateShirtSvg(w: number, h: number): string {
	return `<svg width="${w}" height="${h}" xmlns="http://www.w3.org/2000/svg">
		<rect width="${w}" height="${h}" fill="#0f172a"/>
		<path d="M${w * 0.25},${h * 0.08} L${w * 0.12},${h * 0.12} L${w * 0.04},${h * 0.3} L${w * 0.16},${h * 0.34} L${w * 0.2},${h * 0.22} L${w * 0.2},${h * 0.88} L${w * 0.8},${h * 0.88} L${w * 0.8},${h * 0.22} L${w * 0.84},${h * 0.34} L${w * 0.96},${h * 0.3} L${w * 0.88},${h * 0.12} L${w * 0.75},${h * 0.08} Q${w * 0.65},${h * 0.15} ${w * 0.5},${h * 0.15} Q${w * 0.35},${h * 0.15} ${w * 0.25},${h * 0.08} Z" fill="#1a1a1a" stroke="#334155" stroke-width="2"/>
		<rect x="${w * 0.256}" y="${h * 0.225}" width="${w * 0.488}" height="${h * 0.44}" rx="8" fill="none" stroke="#4f46e5" stroke-width="1" stroke-dasharray="6,4" opacity="0.4"/>
	</svg>`;
}

function generateStickerSvg(w: number, h: number): string {
	return `<svg width="${w}" height="${h}" xmlns="http://www.w3.org/2000/svg">
		<rect width="${w}" height="${h}" fill="#0f172a"/>
		<rect x="${w * 0.08}" y="${h * 0.08}" width="${w * 0.84}" height="${h * 0.84}" rx="32" fill="#1e293b" stroke="#334155" stroke-width="2"/>
		<rect x="${w * 0.12}" y="${h * 0.12}" width="${w * 0.76}" height="${h * 0.76}" rx="24" fill="none" stroke="#4f46e5" stroke-width="1" stroke-dasharray="6,4" opacity="0.4"/>
	</svg>`;
}

// ── Printful API mockup ──

async function printfulMockup(
	designImageUrl: string,
	printfulSku: number,
	variantIds?: number[],
): Promise<Buffer | null> {
	const client = new PrintfulClient();
	if (!client.enabled) return null;

	try {
		const vids = variantIds || [await getDefaultVariant(client, printfulSku)].filter(Boolean) as number[];
		if (!vids.length) return null;

		const technique = printfulSku === 358 ? "sublimation" : "dtg";
		const mockups = await client.generateMockupAndWait(
			printfulSku, vids, designImageUrl, "front", technique, 15, 3000,
		);

		if (!mockups?.length) return null;

		const mockupUrl = mockups[0].mockupUrl;
		if (!mockupUrl) return null;

		// Download the mockup image
		const resp = await fetch(mockupUrl);
		if (!resp.ok) return null;
		return Buffer.from(await resp.arrayBuffer());
	} catch (err) {
		console.warn("[rSwag] Printful mockup failed, using local fallback:", err);
		return null;
	}
}

async function getDefaultVariant(client: PrintfulClient, productId: number): Promise<number | null> {
	try {
		const variants = await client.getCatalogVariants(productId);
		// Pick first black/M variant as default
		const preferred = variants.find(v => v.size === "M" && v.color.toLowerCase().includes("black"));
		return preferred?.id || variants[0]?.id || null;
	} catch {
		return null;
	}
}

// ── Public API ──

/**
 * Generate a product mockup for a design.
 *
 * Tries Printful API first (if available and product has Printful SKU),
 * then falls back to local Sharp compositing.
 */
export async function generateMockup(
	designBuffer: Buffer,
	mockupType: string,
	options?: {
		/** If provided, attempts Printful API mockup first */
		printfulSku?: number;
		/** Public URL to the design (needed for Printful API) */
		designImageUrl?: string;
		/** Skip cache */
		fresh?: boolean;
	},
): Promise<Buffer> {
	const type = mockupType === "poster" ? "poster" : mockupType;
	const cacheKey = `${type}-${designBuffer.length}`;

	if (!options?.fresh) {
		const cached = mockupCache.get(cacheKey);
		if (cached) return cached;
	}

	// Try Printful API if we have a SKU and public URL
	if (options?.printfulSku && options?.designImageUrl) {
		const pfResult = await printfulMockup(
			options.designImageUrl,
			options.printfulSku,
		);
		if (pfResult) {
			mockupCache.set(cacheKey, pfResult);
			evictCache();
			return pfResult;
		}
	}

	// Local fallback
	const result = await localComposite(designBuffer, type);
	mockupCache.set(cacheKey, result);
	evictCache();
	return result;
}

/** List available mockup types. */
export const MOCKUP_TYPES = Object.keys(TEMPLATES);
