import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import type { LetterLayoutConfigValue } from "./LayoutConfigEditor";
import {
	fromCanvasDisplayPosition,
	getLetterCanvasSize,
	type LayoutPositionValue,
	normalizeLayoutPositionValue,
	toCanvasDisplayPosition,
	type LetterLanguage,
} from "./layout-position";
import type { PageMarginValue } from "./PageMarginEditor";
import { normalizePageMarginValue } from "./PageMarginEditor";
import {
	estimateClosingBlockTopPx,
} from "../utils/letter-page-dimensions";

export type LetterAbsoluteAssetKey =
	| "signatures"
	| "seal"
	| "stamp"
	| "senderSignature"
	| "senderStamp";

export const LETTER_ABSOLUTE_ASSET_KEYS: LetterAbsoluteAssetKey[] = [
	"signatures",
	"seal",
	"stamp",
	"senderSignature",
	"senderStamp",
];

export const LETTER_ASSET_PLACEMENT_ELEMENT_ID = "letter-asset-placement";

/** Closing assets shown in style-settings placement UI (sender header assets hidden). */
export const LETTER_PLACEMENT_CANVAS_ASSET_KEYS: LetterAbsoluteAssetKey[] = [
	"signatures",
	"seal",
	"stamp",
];

const ASSET_COLORS: Record<LetterAbsoluteAssetKey, string> = {
	signatures: "border-primary-500 bg-primary-500/20",
	seal: "border-emerald-500 bg-emerald-500/20",
	stamp: "border-amber-500 bg-amber-500/20",
	senderSignature: "border-violet-500 bg-violet-500/20",
	senderStamp: "border-rose-500 bg-rose-500/20",
};

function canvasRectToReference(
	rect: DOMRect,
	clientX: number,
	clientY: number,
	canvasWidth: number,
	canvasHeight: number,
): { x: number; y: number } {
	const scaleX = canvasWidth / rect.width;
	const scaleY = canvasHeight / rect.height;
	return {
		x: (clientX - rect.left) * scaleX,
		y: (clientY - rect.top) * scaleY,
	};
}

function ptToPx(pt: number): number {
	return Math.round((pt * 96) / 72);
}

type LetterAssetsPlacementCanvasProps = {
	value: LetterLayoutConfigValue;
	onChange: (value: LetterLayoutConfigValue) => void;
	disabled?: boolean;
	pageMargin?: PageMarginValue;
	placementLanguage?: LetterLanguage;
	placementFocusNonce?: number;
	/** Preview dialog: larger canvas area, controls below. */
	layoutMode?: "default" | "preview";
};

function isClosingAssetKey(
	key: LetterAbsoluteAssetKey,
): key is "signatures" | "seal" | "stamp" {
	return key === "signatures" || key === "seal" || key === "stamp";
}

function getAssetPosition(
	config: LetterLayoutConfigValue,
	key: LetterAbsoluteAssetKey,
	language: LetterLanguage,
): LayoutPositionValue {
	if (isClosingAssetKey(key)) {
		return config.positionsByLanguage[language][key];
	}
	switch (key) {
		case "senderSignature":
			return config.sender.signature;
		case "senderStamp":
			return config.sender.stamp;
		default: {
			const unreachable: never = key;
			return unreachable;
		}
	}
}

function setAssetPosition(
	config: LetterLayoutConfigValue,
	key: LetterAbsoluteAssetKey,
	position: LayoutPositionValue,
	language: LetterLanguage,
): LetterLayoutConfigValue {
	if (isClosingAssetKey(key)) {
		const positionsByLanguage = {
			...config.positionsByLanguage,
			[language]: {
				...config.positionsByLanguage[language],
				[key]: position,
			},
		};
		const next: LetterLayoutConfigValue = {
			...config,
			positionsByLanguage,
		};
		if (language === "am") {
			if (key === "signatures") {
				next.signatures = { ...config.signatures, position };
			} else if (key === "seal") {
				next.seal = { ...config.seal, position };
			} else if (key === "stamp") {
				next.stamp = { ...config.stamp, position };
			}
		}
		return next;
	}
	switch (key) {
		case "senderSignature":
			return {
				...config,
				sender: { ...config.sender, signature: position },
			};
		case "senderStamp":
			return {
				...config,
				sender: { ...config.sender, stamp: position },
			};
		default: {
			const unreachable: never = key;
			return unreachable;
		}
	}
}

function getAssetSize(
	config: LetterLayoutConfigValue,
	key: LetterAbsoluteAssetKey,
): { widthPx: number; heightPx: number } {
	switch (key) {
		case "signatures":
			return {
				widthPx: ptToPx(config.signatures.widthPt) * 2 + config.signatures.gapPx,
				heightPx: config.signatures.heightPx + 24,
			};
		case "seal":
			return {
				widthPx: config.seal.widthPx,
				heightPx: config.seal.heightPx,
			};
		case "stamp":
			return {
				widthPx: ptToPx(config.stamp.widthPt),
				heightPx: config.stamp.heightPx,
			};
		case "senderSignature":
			return {
				widthPx: ptToPx(config.signatures.widthPt),
				heightPx: config.signatures.heightPx,
			};
		case "senderStamp":
			return {
				widthPx: ptToPx(config.stamp.widthPt),
				heightPx: config.stamp.heightPx,
			};
	}
}

export function normalizeAbsoluteAssetOrder(
	value: unknown,
): LetterAbsoluteAssetKey[] {
	if (!Array.isArray(value)) return [...LETTER_ABSOLUTE_ASSET_KEYS];
	const seen = new Set<LetterAbsoluteAssetKey>();
	const order: LetterAbsoluteAssetKey[] = [];
	for (const item of value) {
		if (
			typeof item === "string" &&
			LETTER_ABSOLUTE_ASSET_KEYS.includes(item as LetterAbsoluteAssetKey) &&
			!seen.has(item as LetterAbsoluteAssetKey)
		) {
			seen.add(item as LetterAbsoluteAssetKey);
			order.push(item as LetterAbsoluteAssetKey);
		}
	}
	for (const key of LETTER_ABSOLUTE_ASSET_KEYS) {
		if (!seen.has(key)) order.push(key);
	}
	return order;
}

export const LetterAssetsPlacementCanvas: React.FC<
	LetterAssetsPlacementCanvasProps
> = ({
	value,
	onChange,
	disabled = false,
	pageMargin,
	placementLanguage = "am",
	placementFocusNonce = 0,
	layoutMode = "default",
}) => {
	const { t } = useTranslation();
	const isPreviewLayout = layoutMode === "preview";
	const canvasRef = useRef<HTMLDivElement>(null);
	const scrollContainerRef = useRef<HTMLDivElement>(null);
	const dragRef = useRef<{ pointerId: number; assetKey: LetterAbsoluteAssetKey } | null>(
		null,
	);
	const [selectedKey, setSelectedKey] =
		useState<LetterAbsoluteAssetKey>("signatures");

	const margins = useMemo(
		() => normalizePageMarginValue(pageMargin),
		[pageMargin],
	);
	const { width: canvasWidth, height: canvasHeight } = useMemo(
		() => getLetterCanvasSize(margins),
		[margins],
	);

	const layerOrder = useMemo(
		() =>
			normalizeAbsoluteAssetOrder(value.absoluteAssetOrder).filter((key) =>
				LETTER_PLACEMENT_CANVAS_ASSET_KEYS.includes(key),
			),
		[value.absoluteAssetOrder],
	);

	const assetLabels = useMemo(
		(): Record<LetterAbsoluteAssetKey, string> => ({
			signatures: t("template.closingAssetSignatures", "Signature block"),
			seal: t("template.closingAssetSeal", "Seal"),
			stamp: t("template.stamp", "Stamp (teeter)"),
			senderSignature: t("template.senderAssetSignature", "Sender signature"),
			senderStamp: t("template.senderAssetStamp", "Sender stamp"),
		}),
		[t],
	);

	const selectedPosition = getAssetPosition(
		value,
		selectedKey,
		placementLanguage,
	);
	const selectedCanvasPosition = toCanvasDisplayPosition(
		selectedKey,
		selectedPosition,
		margins,
	);
	const selectedTop =
		typeof selectedCanvasPosition.top === "number"
			? selectedCanvasPosition.top
			: 0;
	const selectedLeft =
		typeof selectedCanvasPosition.left === "number"
			? selectedCanvasPosition.left
			: 0;

	const closingBlockTop = useMemo(
		() => estimateClosingBlockTopPx(margins),
		[margins],
	);

	const scrollCanvasToAssets = useCallback(() => {
		const container = scrollContainerRef.current;
		if (!container || layerOrder.length === 0) return;

		let minLeft = Infinity;
		let minTop = Infinity;
		let maxRight = -Infinity;
		let maxBottom = -Infinity;

		for (const assetKey of layerOrder) {
			const storedPosition = getAssetPosition(
				value,
				assetKey,
				placementLanguage,
			);
			const position = toCanvasDisplayPosition(
				assetKey,
				storedPosition,
				margins,
			);
			const { widthPx, heightPx } = getAssetSize(value, assetKey);
			const top = typeof position.top === "number" ? position.top : 0;
			const left = typeof position.left === "number" ? position.left : 0;
			minLeft = Math.min(minLeft, left);
			minTop = Math.min(minTop, top);
			maxRight = Math.max(maxRight, left + widthPx);
			maxBottom = Math.max(maxBottom, top + heightPx);
		}

		if (!Number.isFinite(minLeft)) return;

		const boxCenterX = (minLeft + maxRight) / 2;
		const boxCenterY = (minTop + maxBottom) / 2;

		container.scrollLeft = Math.max(
			0,
			boxCenterX - container.clientWidth / 2,
		);
		container.scrollTop = Math.max(
			0,
			boxCenterY - container.clientHeight / 2,
		);
	}, [layerOrder, margins, placementLanguage, value]);

	useEffect(() => {
		const frame = requestAnimationFrame(() => {
			scrollCanvasToAssets();
		});
		return () => cancelAnimationFrame(frame);
	}, [scrollCanvasToAssets, placementLanguage, placementFocusNonce]);

	const placeAsset = useCallback(
		(
			assetKey: LetterAbsoluteAssetKey,
			clientX: number,
			clientY: number,
		) => {
			const canvas = canvasRef.current;
			if (!canvas) return;
			const rect = canvas.getBoundingClientRect();
			const ref = canvasRectToReference(
				rect,
				clientX,
				clientY,
				canvasWidth,
				canvasHeight,
			);
			const { widthPx, heightPx } = getAssetSize(value, assetKey);
			const canvasPosition = normalizeLayoutPositionValue({
				top: Math.round(ref.y - heightPx / 2),
				left: Math.round(ref.x - widthPx / 2),
			});
			const storedPosition = fromCanvasDisplayPosition(
				assetKey,
				canvasPosition,
				margins,
			);
			onChange(setAssetPosition(value, assetKey, storedPosition, placementLanguage));
		},
		[canvasHeight, canvasWidth, margins, onChange, placementLanguage, value],
	);

	const handleAssetPointerDown = (
		event: React.PointerEvent<HTMLDivElement>,
		assetKey: LetterAbsoluteAssetKey,
	) => {
		if (disabled) return;
		event.preventDefault();
		event.stopPropagation();
		setSelectedKey(assetKey);
		dragRef.current = { pointerId: event.pointerId, assetKey };
		event.currentTarget.setPointerCapture(event.pointerId);
		placeAsset(assetKey, event.clientX, event.clientY);
	};

	const handleCanvasPointerDown = (
		event: React.PointerEvent<HTMLDivElement>,
	) => {
		if (disabled) return;
		placeAsset(selectedKey, event.clientX, event.clientY);
	};

	const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
		if (disabled || dragRef.current?.pointerId !== event.pointerId) return;
		placeAsset(dragRef.current.assetKey, event.clientX, event.clientY);
	};

	const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
		if (dragRef.current?.pointerId !== event.pointerId) return;
		dragRef.current = null;
		event.currentTarget.releasePointerCapture(event.pointerId);
	};

	const updateSelectedCoord = (key: "top" | "left", raw: string) => {
		const trimmed = raw.trim();
		if (trimmed === "" || trimmed === "-") return;
		const parsed = Number.parseFloat(trimmed);
		if (!Number.isFinite(parsed)) return;
		const nextCanvas = {
			top: key === "top" ? Math.round(parsed) : selectedTop,
			left: key === "left" ? Math.round(parsed) : selectedLeft,
		};
		const position = fromCanvasDisplayPosition(
			selectedKey,
			{ ...selectedPosition, ...nextCanvas },
			margins,
		);
		onChange(setAssetPosition(value, selectedKey, position, placementLanguage));
	};

	const moveLayer = (index: number, direction: -1 | 1) => {
		const nextIndex = index + direction;
		if (nextIndex < 0 || nextIndex >= layerOrder.length) return;
		const order = [...layerOrder];
		const [item] = order.splice(index, 1);
		order.splice(nextIndex, 0, item);
		const hiddenOrder = normalizeAbsoluteAssetOrder(
			value.absoluteAssetOrder,
		).filter((key) => !LETTER_PLACEMENT_CANVAS_ASSET_KEYS.includes(key));
		onChange({
			...value,
			absoluteAssetOrder: [...order, ...hiddenOrder],
		});
	};

	return (
		<div
			id={LETTER_ASSET_PLACEMENT_ELEMENT_ID}
			className={`space-y-3 rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 p-3 ${
				isPreviewLayout ? "h-full flex flex-col min-h-0" : ""
			}`}
		>
			<div className={isPreviewLayout ? "shrink-0" : undefined}>
				<h5 className="text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wide">
					{t("template.assetPlacementCanvas", "Asset placement")}
				</h5>
				<p className="text-[10px] text-gray-500 dark:text-gray-400 mt-1">
					{t(
						"template.assetPlacementCanvasHint",
						"Drag each asset on the page. Editing positions for {{language}}. Canvas matches PDF content area ({{width}}×{{height}} px).",
						{
							language:
								placementLanguage === "am"
									? t("template.languageAm", "Amharic")
									: t("template.languageEn", "English"),
							width: canvasWidth,
							height: canvasHeight,
						},
					)}
				</p>
			</div>

			<div
				className={
					isPreviewLayout
						? "flex flex-col gap-3 flex-1 min-h-0"
						: "grid grid-cols-1 lg:grid-cols-[1fr_220px] gap-3"
				}
			>
				<div
					ref={scrollContainerRef}
					className={
						isPreviewLayout
							? "flex-1 min-h-0 overflow-auto rounded-md border border-gray-200 dark:border-gray-600 bg-gray-100 dark:bg-gray-900/50 p-3 flex items-start justify-center"
							: "overflow-auto max-h-[560px] rounded-md border border-gray-200 dark:border-gray-600 bg-gray-100 dark:bg-gray-900/50 p-2"
					}
				>
					<div
						ref={canvasRef}
						className={`relative touch-none select-none bg-white dark:bg-gray-900/60 ${
							disabled ? "cursor-not-allowed opacity-60" : "cursor-crosshair"
						}`}
						style={{
							width: canvasWidth,
							height: canvasHeight,
							minWidth: canvasWidth,
							minHeight: canvasHeight,
							overflow: "visible",
						}}
						onPointerDown={handleCanvasPointerDown}
						onPointerMove={handlePointerMove}
						onPointerUp={handlePointerUp}
						onPointerCancel={handlePointerUp}
					>
						<div className="absolute inset-0 border border-dashed border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900/40 pointer-events-none" />
						<div
							className="absolute left-0 right-0 border-t border-dashed border-purple-300 dark:border-purple-600 pointer-events-none"
							style={{
								top: `${(closingBlockTop / canvasHeight) * 100}%`,
							}}
							title={t("template.closingBlockGuide", "Sincerely section (approx.)")}
						/>
						{layerOrder.map((assetKey, layerIndex) => {
							const storedPosition = getAssetPosition(
								value,
								assetKey,
								placementLanguage,
							);
							const position = toCanvasDisplayPosition(
								assetKey,
								storedPosition,
								margins,
							);
							const { widthPx, heightPx } = getAssetSize(value, assetKey);
							const top =
								typeof position.top === "number" ? position.top : 0;
							const left =
								typeof position.left === "number" ? position.left : 0;
							const isSelected = selectedKey === assetKey;
							return (
								<div
									key={assetKey}
									className={`absolute rounded border-2 touch-none ${
										ASSET_COLORS[assetKey]
									} ${isSelected ? "ring-2 ring-purple-500 ring-offset-1" : ""} ${
										disabled
											? "cursor-not-allowed"
											: "cursor-grab active:cursor-grabbing"
									}`}
									style={{
										left: `${(left / canvasWidth) * 100}%`,
										top: `${(top / canvasHeight) * 100}%`,
										width: `${(widthPx / canvasWidth) * 100}%`,
										height: `${(heightPx / canvasHeight) * 100}%`,
										zIndex: layerIndex + 1,
										pointerEvents: disabled ? "none" : "auto",
									}}
									onPointerDown={(event) =>
										handleAssetPointerDown(event, assetKey)
									}
								>
									<span className="absolute left-0.5 top-0.5 text-[8px] font-medium text-gray-700 dark:text-gray-200 bg-white/80 dark:bg-gray-900/80 px-1 rounded pointer-events-none">
										{assetLabels[assetKey]}
									</span>
								</div>
							);
						})}
					</div>
				</div>

				<div
					className={
						isPreviewLayout
							? "shrink-0 grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-3"
							: "space-y-3"
					}
				>
					<div>
						<p className="text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">
							{t("template.selectedAsset", "Selected asset")}
						</p>
						<select
							disabled={disabled}
							value={selectedKey}
							onChange={(e) =>
								setSelectedKey(e.target.value as LetterAbsoluteAssetKey)
							}
							className="w-full px-2 py-1.5 border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 rounded text-sm"
						>
							{LETTER_PLACEMENT_CANVAS_ASSET_KEYS.map((key) => (
								<option key={key} value={key}>
									{assetLabels[key]}
								</option>
							))}
						</select>
					</div>

					<div className="grid grid-cols-2 gap-2">
						<div>
							<label className="block text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">
								{t("template.positionTop", "Top (px)")}
							</label>
							<input
								type="number"
								step={1}
								disabled={disabled}
								value={selectedTop}
								onChange={(e) => updateSelectedCoord("top", e.target.value)}
								className="w-full px-2 py-1.5 border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 rounded text-sm"
							/>
						</div>
						<div>
							<label className="block text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">
								{t("template.positionLeft", "Left (px)")}
							</label>
							<input
								type="number"
								step={1}
								disabled={disabled}
								value={selectedLeft}
								onChange={(e) => updateSelectedCoord("left", e.target.value)}
								className="w-full px-2 py-1.5 border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 rounded text-sm"
							/>
						</div>
					</div>

					<div>
						<p className="text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">
							{t("template.layerOrder", "Layer order (back → front)")}
						</p>
						<ul className="space-y-1">
							{layerOrder.map((key, index) => (
								<li
									key={key}
									className={`flex items-center justify-between gap-1 px-2 py-1 rounded border text-xs ${
										selectedKey === key
											? "border-purple-400 bg-purple-50 dark:bg-purple-900/20"
											: "border-gray-200 dark:border-gray-600"
									}`}
								>
									<button
										type="button"
										disabled={disabled}
										onClick={() => setSelectedKey(key)}
										className="text-left flex-1 truncate text-gray-700 dark:text-gray-200"
									>
										{assetLabels[key]}
									</button>
									<div className="flex gap-0.5 shrink-0">
										<button
											type="button"
											disabled={disabled || index === 0}
											onClick={() => moveLayer(index, -1)}
											className="px-1.5 py-0.5 rounded border border-gray-300 dark:border-gray-500 disabled:opacity-40"
											title={t("template.sendBackward", "Send backward")}
										>
											↓
										</button>
										<button
											type="button"
											disabled={disabled || index === layerOrder.length - 1}
											onClick={() => moveLayer(index, 1)}
											className="px-1.5 py-0.5 rounded border border-gray-300 dark:border-gray-500 disabled:opacity-40"
											title={t("template.bringForward", "Bring forward")}
										>
											↑
										</button>
									</div>
								</li>
							))}
						</ul>
					</div>
				</div>
			</div>
		</div>
	);
};
