import {
	ArrowBendUpLeft,
	ArrowBendUpRight,
	Copy,
	DotsThree,
	Link,
	PencilSimple,
	PushPin,
	Smiley,
	Trash,
} from '@phosphor-icons/react';
import { useEffect, useLayoutEffect, useRef, useState, type MouseEvent } from 'react';
import { createPortal } from 'react-dom';
import { getTwemojiUrl } from '../../utils/twemoji';
import styles from './MessageActionBar.module.css';

// Default quick-reaction emojis shown as the first three icons on the
// hover bar. Matches the new UI's set.
const QUICK_EMOJIS: Array<{ emoji: string; name: string }> = [
	{ emoji: '😄', name: 'smile' },
	{ emoji: '👍', name: 'thumbsup' },
	{ emoji: '👌', name: 'ok_hand' },
];

interface MessageActionBarProps {
	isOwnMessage: boolean;
	onReply?: () => void;
	onEdit?: () => void;
	onDelete?: () => void;
	onReact?: (e?: MouseEvent<HTMLButtonElement>) => void;
	onQuickReact?: (emoji: string) => void;
	onPin?: () => void;
	onCopyText?: () => void;
	onCopyLink?: () => void;
	onForward?: () => void;
	/**
	 * External trigger for opening the More dropdown (e.g. right-click
	 * on a message). When set, the dropdown opens at `{ x, y }` and the
	 * hover action bar stays visible via the `.actionBarForceVisible`
	 * class on the parent. Pass null to close.
	 */
	externalMenuAt?: { x: number; y: number } | null;
	onExternalMenuClose?: () => void;
	/**
	 * Fires when the local More menu opens / closes so the parent
	 * (MessageGroup) can keep the hover bar pinned to visible while
	 * the dropdown is on screen. Without this the bar disappears as
	 * soon as the pointer leaves the message row.
	 */
	onMenuOpenChange?: (isOpen: boolean) => void;
}

/**
 * Hover quick-action bar + right-click context menu.
 *
 * Appears via `:global(.messageHoverable):hover > .container` — must be
 * rendered as a direct child of an element with the `messageHoverable`
 * class.
 *
 * The "More" button opens a portal-rendered dropdown with additional
 * actions (edit/delete/pin/copy/copy link).
 */
export function MessageActionBar({
	isOwnMessage,
	onReply,
	onEdit,
	onDelete,
	onReact,
	onQuickReact,
	onPin,
	onCopyText,
	onCopyLink,
	onForward,
	externalMenuAt,
	onExternalMenuClose,
	onMenuOpenChange,
}: MessageActionBarProps) {
	const moreButtonRef = useRef<HTMLButtonElement>(null);
	const menuRef = useRef<HTMLDivElement>(null);
	const [menuAt, setMenuAt] = useState<{ x: number; y: number } | null>(null);
	// Adjusted position after we measure the menu's actual size and
	// clamp it to the viewport. Falls back to the requested coords
	// while the measurement is in flight (one frame at most).
	const [adjustedPos, setAdjustedPos] = useState<
		{ top: number; left: number } | null
	>(null);

	// Merge external right-click menu state with local more-button state.
	const effectiveMenuAt = externalMenuAt ?? menuAt;

	// Notify the parent whenever the menu open state flips, so the
	// message row can pin the hover bar visible while the dropdown is
	// up. Triggered for both local and external menus.
	useEffect(() => {
		onMenuOpenChange?.(!!effectiveMenuAt);
	}, [effectiveMenuAt, onMenuOpenChange]);

	useEffect(() => {
		if (!effectiveMenuAt) return;
		const onKey = (e: KeyboardEvent) => {
			if (e.key === 'Escape') closeMenu();
		};
		document.addEventListener('keydown', onKey);
		return () => document.removeEventListener('keydown', onKey);
	}, [effectiveMenuAt]);

	// Clamp the menu inside the viewport. When the requested top/left
	// would push the menu off-screen, flip vertically (open upwards)
	// or shift horizontally so it stays fully visible.
	useLayoutEffect(() => {
		if (!effectiveMenuAt) {
			setAdjustedPos(null);
			return;
		}
		const measure = () => {
			const el = menuRef.current;
			if (!el) return;
			const rect = el.getBoundingClientRect();
			const margin = 8;
			const vw = window.innerWidth;
			const vh = window.innerHeight;
			let top = effectiveMenuAt.y;
			let left = effectiveMenuAt.x - 180;
			// Vertical: flip upwards if the menu would overflow the
			// bottom of the viewport. Use the menu's measured height
			// so the flip lines its bottom up with the requested y.
			if (top + rect.height + margin > vh) {
				top = Math.max(margin, effectiveMenuAt.y - rect.height);
			}
			top = Math.max(margin, Math.min(top, vh - rect.height - margin));
			// Horizontal: shift left if it would overflow the right
			// edge, then clamp at the left margin.
			if (left + rect.width + margin > vw) {
				left = vw - rect.width - margin;
			}
			left = Math.max(margin, left);
			setAdjustedPos({ top, left });
		};
		// One frame to let the menu mount, one fallback in case
		// requestAnimationFrame fires before layout settles.
		const raf = requestAnimationFrame(measure);
		return () => cancelAnimationFrame(raf);
	}, [effectiveMenuAt]);

	const closeMenu = () => {
		setMenuAt(null);
		onExternalMenuClose?.();
	};

	const openMoreMenu = () => {
		const rect = moreButtonRef.current?.getBoundingClientRect();
		if (!rect) return;
		setMenuAt({ x: rect.right, y: rect.bottom + 4 });
	};

	const runAndClose = (handler?: () => void) => () => {
		handler?.();
		closeMenu();
	};

	return (
		<div className={styles.container}>
			{onQuickReact &&
				QUICK_EMOJIS.map((qe) => (
					<button
						key={qe.name}
						type="button"
						className={styles.button}
						onClick={() => onQuickReact(qe.emoji)}
						aria-label={`React with :${qe.name}:`}
						title={`:${qe.name}:`}
					>
						<img
							src={getTwemojiUrl(qe.emoji)}
							alt={qe.emoji}
							className={styles.quickEmoji}
							draggable={false}
						/>
					</button>
				))}
			<button
				type="button"
				className={styles.button}
				onClick={(e) => onReact?.(e)}
				aria-label="Add reaction"
				title="Add Reaction"
			>
				<Smiley size={20} />
			</button>
			<button
				type="button"
				className={styles.button}
				onClick={onReply}
				aria-label="Reply"
				title="Reply"
			>
				<ArrowBendUpLeft size={20} />
			</button>
			{isOwnMessage && onEdit && (
				<button
					type="button"
					className={styles.button}
					onClick={onEdit}
					aria-label="Edit"
					title="Edit"
				>
					<PencilSimple size={20} />
				</button>
			)}
			<button
				type="button"
				ref={moreButtonRef}
				className={styles.button}
				onClick={openMoreMenu}
				aria-label="More"
				title="More"
			>
				<DotsThree size={20} weight="bold" />
			</button>

			{effectiveMenuAt &&
				createPortal(
					<>
						<div
							style={{ position: 'fixed', inset: 0, zIndex: 14999 }}
							onClick={closeMenu}
							onContextMenu={(e) => {
								e.preventDefault();
								closeMenu();
							}}
						/>
						<div
							ref={menuRef}
							className={styles.moreMenu}
							style={{
								position: 'fixed',
								top: adjustedPos?.top ?? effectiveMenuAt.y,
								left: adjustedPos?.left ?? effectiveMenuAt.x - 180,
								// Hide the menu for the first frame while we
								// measure it; revealing it after the clamp
								// avoids a flash at the wrong position.
								visibility: adjustedPos ? 'visible' : 'hidden',
								zIndex: 15000,
							}}
							onClick={(e) => e.stopPropagation()}
						>
							{onReply && (
								<button
									type="button"
									className={styles.menuItem}
									onClick={runAndClose(onReply)}
								>
									<ArrowBendUpLeft size={16} />
									<span>Reply</span>
								</button>
							)}
							{onForward && (
								<button
									type="button"
									className={styles.menuItem}
									onClick={runAndClose(onForward)}
								>
									<ArrowBendUpRight size={16} />
									<span>Forward</span>
								</button>
							)}
							{onPin && (
								<button
									type="button"
									className={styles.menuItem}
									onClick={runAndClose(onPin)}
								>
									<PushPin size={16} />
									<span>Pin Message</span>
								</button>
							)}
							{onCopyText && (
								<button
									type="button"
									className={styles.menuItem}
									onClick={runAndClose(onCopyText)}
								>
									<Copy size={16} />
									<span>Copy Text</span>
								</button>
							)}
							{onCopyLink && (
								<button
									type="button"
									className={styles.menuItem}
									onClick={runAndClose(onCopyLink)}
								>
									<Link size={16} />
									<span>Copy Link</span>
								</button>
							)}
							{isOwnMessage && onEdit && (
								<button
									type="button"
									className={styles.menuItem}
									onClick={runAndClose(onEdit)}
								>
									<PencilSimple size={16} />
									<span>Edit Message</span>
								</button>
							)}
							{isOwnMessage && onDelete && (
								<>
									<div className={styles.menuDivider} />
									<button
										type="button"
										className={`${styles.menuItem} ${styles.menuItemDanger}`}
										onClick={runAndClose(onDelete)}
									>
										<Trash size={16} />
										<span>Delete Message</span>
									</button>
								</>
							)}
						</div>
					</>,
					document.body,
				)}
		</div>
	);
}
