<script lang="ts">
	import { onMount } from 'svelte';

	interface Props {
		file: File;
		onDone: (result: {
			imageBlob: Blob;
			maskPath: string;
			imageWidth: number;
			imageHeight: number;
			borderColor: string;
			borderThickness: number;
			shortname: string;
		}) => void;
		onCancel: () => void;
	}

	let { file, onDone, onCancel }: Props = $props();

	// Form state
	let shortname = $state('');
	let borderColor = $state('#ff99cc');
	let borderThickness = $state(8);
	let borderEnabled = $state(false);
	let processing = $state(false);

	// Canvas + image
	let canvas: HTMLCanvasElement;
	let img: HTMLImageElement;
	let imgLoaded = $state(false);
	let displayW = 0;
	let displayH = 0;
	let scaleX = 1;
	let scaleY = 1;

	// Drawing state
	type Point = { x: number; y: number };
	let points = $state<Point[]>([]);
	let isDrawing = $state(false);
	let pathClosed = $state(false);
	const CLOSE_RADIUS = 14; // px — snap-close distance

	onMount(() => {
		shortname = file.name.replace(/\.[^.]+$/, '').slice(0, 50);
		img = new Image();
		img.onload = () => {
			imgLoaded = true;
			// Fit image into max 520×420 display area
			const maxW = 520;
			const maxH = 420;
			const ratio = Math.min(maxW / img.naturalWidth, maxH / img.naturalHeight, 1);
			displayW = Math.round(img.naturalWidth * ratio);
			displayH = Math.round(img.naturalHeight * ratio);
			scaleX = img.naturalWidth / displayW;
			scaleY = img.naturalHeight / displayH;
			canvas.width = displayW;
			canvas.height = displayH;
			redraw();
		};
		img.src = URL.createObjectURL(file);
		return () => URL.revokeObjectURL(img.src);
	});

	$effect(() => {
		// Redraw preview when border settings change
		const _deps = [borderEnabled, borderColor, borderThickness, pathClosed, points.length];
		if (imgLoaded) redraw();
	});

	function redraw() {
		if (!canvas || !img) return;
		const ctx = canvas.getContext('2d')!;
		ctx.clearRect(0, 0, displayW, displayH);

		// Draw image dimmed
		ctx.globalAlpha = pathClosed ? 0.35 : 0.75;
		ctx.drawImage(img, 0, 0, displayW, displayH);
		ctx.globalAlpha = 1;

		if (points.length < 2) return;

		// Draw the current/closed path
		ctx.beginPath();
		ctx.moveTo(points[0].x, points[0].y);
		for (let i = 1; i < points.length; i++) {
			const prev = points[i - 1];
			const curr = points[i];
			// Smooth with midpoints
			ctx.quadraticCurveTo(prev.x, prev.y, (prev.x + curr.x) / 2, (prev.y + curr.y) / 2);
		}
		if (pathClosed) ctx.closePath();

		// If closed: border outside, then image clipped inside
		if (pathClosed) {
			// Border first (before clip) so it sits outside the image
			if (borderEnabled && borderThickness > 0) {
				ctx.strokeStyle = borderColor;
				ctx.lineWidth = borderThickness * 2;
				ctx.lineJoin = 'round';
				ctx.lineCap = 'round';
				ctx.stroke();
			}
			// Clip and draw image on top, covering the inner half of the stroke
			ctx.save();
			ctx.clip();
			ctx.globalAlpha = 1;
			ctx.drawImage(img, 0, 0, displayW, displayH);
			ctx.restore();
		}

		// Draw path outline
		ctx.strokeStyle = pathClosed ? 'rgba(255,255,255,0.6)' : '#ffffff';
		ctx.lineWidth = pathClosed ? 1 : 2;
		ctx.setLineDash(pathClosed ? [] : [6, 3]);
		ctx.lineJoin = 'round';
		ctx.lineCap = 'round';
		ctx.stroke();
		ctx.setLineDash([]);

		// Close-snap indicator when near start
		if (!pathClosed && points.length > 3) {
			ctx.beginPath();
			ctx.arc(points[0].x, points[0].y, CLOSE_RADIUS, 0, Math.PI * 2);
			ctx.strokeStyle = 'rgba(255,255,100,0.7)';
			ctx.lineWidth = 1.5;
			ctx.stroke();
		}
	}

	function getPos(e: MouseEvent | TouchEvent): Point {
		const rect = canvas.getBoundingClientRect();
		const src = e instanceof TouchEvent ? e.touches[0] : e;
		return {
			x: (src.clientX - rect.left) * (displayW / rect.width),
			y: (src.clientY - rect.top) * (displayH / rect.height)
		};
	}

	function isNearStart(p: Point): boolean {
		if (points.length < 4) return false;
		const dx = p.x - points[0].x;
		const dy = p.y - points[0].y;
		return Math.sqrt(dx * dx + dy * dy) < CLOSE_RADIUS;
	}

	function startDraw(e: MouseEvent | TouchEvent) {
		e.preventDefault();
		if (pathClosed) return;
		isDrawing = true;
		const p = getPos(e);
		points = [p];
	}

	function continueDraw(e: MouseEvent | TouchEvent) {
		e.preventDefault();
		if (!isDrawing || pathClosed) return;
		const p = getPos(e);
		// Sub-sample: only add point if moved enough
		const last = points[points.length - 1];
		const dx = p.x - last.x;
		const dy = p.y - last.y;
		if (dx * dx + dy * dy < 9) return;
		points = [...points, p];
		redraw();
	}

	function endDraw(e: MouseEvent | TouchEvent) {
		e.preventDefault();
		if (!isDrawing) return;
		isDrawing = false;
		const p = getPos(e);
		if (isNearStart(p) || points.length > 8) {
			pathClosed = true;
		}
		redraw();
	}

	function closePath() {
		if (points.length > 2) {
			pathClosed = true;
			redraw();
		}
	}

	function resetPath() {
		points = [];
		pathClosed = false;
		redraw();
	}

	async function buildImageAndPath(): Promise<{
		imageBlob: Blob;
		maskPath: string;
		imageWidth: number;
		imageHeight: number;
	}> {
		// Bounding box of drawn path in display pixels
		const xs = points.map((p) => p.x);
		const ys = points.map((p) => p.y);
		const bboxX = Math.max(0, Math.floor(Math.min(...xs)));
		const bboxY = Math.max(0, Math.floor(Math.min(...ys)));
		const bboxMaxX = Math.min(displayW, Math.ceil(Math.max(...xs)));
		const bboxMaxY = Math.min(displayH, Math.ceil(Math.max(...ys)));

		// Scale to natural image pixel crop region
		const cropX = Math.round(bboxX * scaleX);
		const cropY = Math.round(bboxY * scaleY);
		const cropW = Math.round((bboxMaxX - bboxX) * scaleX);
		const cropH = Math.round((bboxMaxY - bboxY) * scaleY);

		// Build SVG path in natural image pixels, relative to crop origin
		const sx = (x: number) => Math.round((x - bboxX) * scaleX);
		const sy = (y: number) => Math.round((y - bboxY) * scaleY);
		let d = `M ${sx(points[0].x)} ${sy(points[0].y)}`;
		for (let i = 1; i < points.length; i++) {
			const prev = points[i - 1];
			const curr = points[i];
			d += ` Q ${sx(prev.x)} ${sy(prev.y)} ${sx((prev.x + curr.x) / 2)} ${sy((prev.y + curr.y) / 2)}`;
		}
		d += ' Z';

		// Crop to bbox, then mask out pixels outside the drawn path using destination-in compositing.
		// Exported as PNG to preserve transparency.
		const out = document.createElement('canvas');
		out.width = cropW;
		out.height = cropH;
		const outCtx = out.getContext('2d')!;
		outCtx.drawImage(img, cropX, cropY, cropW, cropH, 0, 0, cropW, cropH);

		// Keep only pixels inside the mask path; clear everything outside
		outCtx.globalCompositeOperation = 'destination-in';
		outCtx.fill(new Path2D(d));
		outCtx.globalCompositeOperation = 'source-over';

		const imageBlob = await new Promise<Blob>((resolve, reject) =>
			out.toBlob((b) => (b ? resolve(b) : reject(new Error('toBlob failed'))), 'image/png')
		);

		return { imageBlob, maskPath: d, imageWidth: cropW, imageHeight: cropH };
	}

	async function handleSubmit() {
		if (!shortname.trim() || !pathClosed) return;
		processing = true;
		try {
			const { imageBlob, maskPath, imageWidth, imageHeight } = await buildImageAndPath();
			onDone({
				imageBlob,
				maskPath,
				imageWidth,
				imageHeight,
				borderColor: borderEnabled ? borderColor : '',
				// Store border thickness in natural image pixels so SVG can use directly
				borderThickness: borderEnabled ? Math.round(borderThickness * scaleX) : 0,
				shortname: shortname.trim()
			});
		} finally {
			processing = false;
		}
	}
</script>

<div class="editor">
	<h2>create sticker</h2>

	<div class="editor-body">
		<!-- Drawing canvas -->
		<div class="canvas-wrap">
			{#if !imgLoaded}
				<div class="canvas-placeholder">loading…</div>
			{/if}
			<canvas
				bind:this={canvas}
				class:hidden={!imgLoaded}
				onmousedown={startDraw}
				onmousemove={continueDraw}
				onmouseup={endDraw}
				onmouseleave={endDraw}
				ontouchstart={startDraw}
				ontouchmove={continueDraw}
				ontouchend={endDraw}
			></canvas>
			<div class="canvas-hint">
				{#if !imgLoaded}
					&nbsp;
				{:else if pathClosed}
					✓ shape drawn
				{:else if points.length > 3}
					draw back to the dot to close, or click "close shape"
				{:else}
					click and drag to draw your sticker shape
				{/if}
			</div>
		</div>

		<!-- Controls -->
		<div class="controls">
			<label class="field">
				<span>shortname</span>
				<input type="text" bind:value={shortname} maxlength="50" placeholder="my cool sticker" />
			</label>

			<label class="field checkbox-field">
				<input type="checkbox" bind:checked={borderEnabled} />
				<span>add border</span>
			</label>

			{#if borderEnabled}
				<label class="field">
					<span>border color</span>
					<div class="color-row">
						<input type="color" bind:value={borderColor} />
						<span class="hex">{borderColor}</span>
					</div>
				</label>
				<label class="field">
					<span>thickness: {borderThickness}px</span>
					<input type="range" min="1" max="30" bind:value={borderThickness} />
				</label>
			{/if}

			<div class="path-actions">
				{#if !pathClosed && points.length > 2}
					<button class="btn-outline" onclick={closePath}>close shape</button>
				{/if}
				{#if points.length > 0}
					<button class="btn-outline" onclick={resetPath}>redraw</button>
				{/if}
			</div>

			<div class="actions">
				<button class="btn-cancel" onclick={onCancel}>cancel</button>
				<button
					class="btn-submit"
					onclick={handleSubmit}
					disabled={processing || !shortname.trim() || !pathClosed}
				>
					{processing ? 'processing…' : 'use this sticker →'}
				</button>
			</div>
		</div>
	</div>
</div>

<style>
	.editor {
		background: #fffef8;
		border: 2px solid #e0d4c0;
		border-radius: 2px;
		padding: 1.5rem;
		box-shadow: 3px 4px 12px rgba(0, 0, 0, 0.12);
		max-width: 900px;
		margin: 0 auto;
	}

	h2 {
		font-family: 'Caveat', cursive;
		font-size: 2rem;
		margin: 0 0 1.2rem;
		color: #5a4a6a;
		transform: rotate(-0.5deg);
	}

	.editor-body {
		display: grid;
		grid-template-columns: 1fr auto;
		gap: 1.5rem;
		align-items: start;
	}

	@media (max-width: 680px) {
		.editor-body {
			grid-template-columns: 1fr;
		}
	}

	.canvas-wrap {
		display: flex;
		flex-direction: column;
		gap: 0.4rem;
	}

	canvas {
		display: block;
		cursor: crosshair;
		border: 1.5px solid #d0c8b8;
		border-radius: 2px;
		background: #1a1a1a;
		max-width: 100%;
		touch-action: none;
	}

	canvas.hidden {
		display: none;
	}

	.canvas-placeholder {
		width: 300px;
		height: 200px;
		background: #1a1a1a;
		border: 1.5px solid #d0c8b8;
		border-radius: 2px;
		display: flex;
		align-items: center;
		justify-content: center;
		font-family: 'Caveat', cursive;
		color: #666;
	}

	.canvas-hint {
		font-family: 'Caveat', cursive;
		font-size: 0.9rem;
		color: #999;
		min-height: 1.2em;
	}

	.controls {
		display: flex;
		flex-direction: column;
		gap: 0.8rem;
		min-width: 200px;
	}

	.field {
		display: flex;
		flex-direction: column;
		gap: 0.3rem;
		font-family: 'Caveat', cursive;
		font-size: 1rem;
		color: #555;
	}

	.checkbox-field {
		flex-direction: row;
		align-items: center;
		gap: 0.5rem;
	}

	.field input[type='text'],
	.field input[type='range'] {
		font-family: 'Caveat', cursive;
		font-size: 1rem;
		padding: 0.3rem 0.5rem;
		border: 1.5px solid #c8c0b0;
		border-radius: 2px;
		background: white;
		outline: none;
	}

	.field input[type='text']:focus {
		border-color: #a88ad8;
	}

	.color-row {
		display: flex;
		align-items: center;
		gap: 0.5rem;
	}

	.hex {
		font-family: monospace;
		font-size: 0.85rem;
		color: #888;
	}

	.path-actions {
		display: flex;
		gap: 0.4rem;
		flex-wrap: wrap;
	}

	.btn-outline {
		font-family: 'Caveat', cursive;
		font-size: 0.95rem;
		padding: 0.3rem 0.7rem;
		background: transparent;
		border: 1.5px solid #c4a8e8;
		border-radius: 2px;
		cursor: pointer;
		color: #6a4a8a;
	}

	.btn-outline:hover {
		background: #f0e8ff;
	}

	.actions {
		display: flex;
		gap: 0.6rem;
		margin-top: 0.5rem;
	}

	.btn-cancel {
		font-family: 'Caveat', cursive;
		font-size: 1rem;
		padding: 0.4rem 0.9rem;
		background: transparent;
		border: 1.5px solid #ccc;
		border-radius: 2px;
		cursor: pointer;
		color: #888;
	}

	.btn-submit {
		font-family: 'Caveat', cursive;
		font-size: 1rem;
		padding: 0.4rem 1rem;
		background: #d0e8c0;
		border: 2px solid #a8c890;
		border-radius: 2px;
		cursor: pointer;
		color: #3a5a2a;
		flex: 1;
	}

	.btn-submit:hover:not(:disabled) {
		background: #bcd8ac;
	}

	.btn-submit:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}
</style>
