import { mergeAttributes, Node } from '@tiptap/core';
import {
	NodeViewWrapper,
	ReactNodeViewRenderer,
	type ReactNodeViewProps,
} from '@tiptap/react';
import { __ } from '@wordpress/i18n';
import { Expand, Pencil, Trash2, X } from 'lucide-react';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';

declare const wp: {
	media: (options: {
		title?: string;
		button?: { text: string };
		multiple?: boolean;
		library?: { type?: string };
	}) => {
		on: (event: string, cb: () => void) => void;
		open: () => void;
		state: () => {
			get: (key: string) => {
				first: () => { toJSON: () => { url: string; filename: string } };
			};
		};
	};
};

function openWpMedia(
	libraryType: string,
	title: string,
	onSelect: (url: string, filename: string) => void,
) {
	if (typeof wp === 'undefined' || typeof wp.media !== 'function') {
		console.error(
			'wp.media is not available. Ensure wp_enqueue_media() is called.',
		);
		return;
	}
	const frame = wp.media({
		title,
		button: { text: __('Use this file', 'allcoach') },
		multiple: false,
		library: { type: libraryType },
	});
	frame.on('select', () => {
		const attachment = frame.state().get('selection').first().toJSON();
		onSelect(attachment.url, attachment.filename);
	});
	frame.open();
}

type Attrs = { url: string; title: string };

type MediaFieldConfig = {
	/** Node name, e.g. 'imageBlock' */
	name: string;
	/** HTML data-type attribute, e.g. 'image-block' */
	dataType: string;
	/** wp.media library filter, e.g. 'image' */
	libraryType: 'image' | 'video' | 'audio';
	/** Badge / dialog title label, e.g. 'Add Image' */
	addLabel: string;
	/** Subtitle shown in the empty state */
	subLabel: string;
	/** Lucide icon component for the empty state */
	icon: React.ElementType;
	/** Insert command name, e.g. 'insertImageBlock' */
	commandName: string;
};

export function createMediaField({
	name,
	dataType,
	libraryType,
	addLabel,
	subLabel,
	icon,
	commandName,
}: MediaFieldConfig) {
	return Node.create({
		name,
		group: 'block',
		atom: true,
		selectable: true,
		draggable: true,

		addAttributes() {
			return {
				url: {
					default: '',
					renderHTML: (attrs) => ({ 'data-url': attrs.url }),
					parseHTML: (el) => el.getAttribute('data-url') ?? '',
				},
				title: {
					default: '',
					renderHTML: (attrs) => ({ 'data-title': attrs.title }),
					parseHTML: (el) => el.getAttribute('data-title') ?? '',
				},
				autoFocus: { default: false, renderHTML: () => ({}) },
			};
		},

		parseHTML() {
			return [{ tag: `div[data-type="${dataType}"]` }];
		},

		renderHTML({ HTMLAttributes }) {
			return [
				'div',
				mergeAttributes(HTMLAttributes, { 'data-type': dataType }),
			];
		},

		addNodeView() {
			return ReactNodeViewRenderer((props: ReactNodeViewProps) => (
				<MediaFieldComponent
					{...props}
					libraryType={libraryType}
					addLabel={addLabel}
					subLabel={subLabel}
					icon={icon}
				/>
			));
		},

		addCommands() {
			return {
				[commandName]:
					(attrs: Partial<Attrs> = {}) =>
					({ chain }: any) =>
						chain()
							.insertContent({
								type: name,
								attrs: {
									url: attrs.url ?? '',
									title: attrs.title ?? '',
									autoFocus: true,
								},
							})
							.run(),
			};
		},
	});
}

type MediaFieldComponentProps = ReactNodeViewProps & {
	libraryType: 'image' | 'video' | 'audio';
	addLabel: string;
	subLabel: string;
	icon: React.ElementType;
};

function MediaFieldComponent({
	node,
	updateAttributes,
	libraryType,
	addLabel,
	subLabel,
	icon: Icon,
}: MediaFieldComponentProps) {
	const { url, title, autoFocus } = node.attrs as Attrs & {
		autoFocus: boolean;
	};
	const hasMedia = !!url;
	const [lightbox, setLightbox] = useState(false);

	// Open the picker immediately when freshly inserted
	useEffect(() => {
		if (autoFocus) {
			updateAttributes({ autoFocus: false });
			// handleOpen();
		}
	}, []);

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

	const handleOpen = () => {
		openWpMedia(libraryType, addLabel, (newUrl, filename) => {
			updateAttributes({ url: newUrl, title: title || filename });
		});
	};

	const handleRemove = () => updateAttributes({ url: '', title: '' });

	return (
		<NodeViewWrapper className="rounded border bg-gray-50 p-3">
			<div className="rounded-[9px] border border-gray-200 bg-white transition-colors focus-within:border-teal-400">
				{/* Header */}
				<div className="group/title flex items-center gap-3 px-4 py-3">
					<div className="relative flex min-w-0 flex-1 items-center">
						<input
							value={title}
							onChange={(e) => updateAttributes({ title: e.target.value })}
							placeholder={
								url
									? (url.split('/').pop()?.split('?')[0] ?? addLabel)
									: addLabel
							}
							className="min-w-0 flex-1 rounded-md bg-transparent py-0.5 pr-6 pl-2 text-[13px] font-semibold text-gray-800 outline-none placeholder:font-normal placeholder:text-gray-400 hover:bg-gray-100 focus:bg-gray-100"
						/>
						<Pencil className="pointer-events-none absolute right-1.5 size-3 text-gray-400 opacity-0 transition-opacity group-hover/title:opacity-100" />
					</div>
					{hasMedia && (
						<div className="ml-auto flex shrink-0 items-center gap-1">
							{libraryType === 'image' && (
								<button
									type="button"
									onClick={() => setLightbox(true)}
									className="flex items-center justify-center rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600"
									title={__('View full size', 'allcoach')}
								>
									<Expand className="size-3.5" />
								</button>
							)}
							<button
								type="button"
								onClick={handleOpen}
								className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"
							>
								<Pencil className="size-3" />
								{__('Replace', 'allcoach')}
							</button>
							<button
								type="button"
								onClick={handleRemove}
								className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-medium text-red-400 hover:bg-red-50 hover:text-red-600"
							>
								<Trash2 className="size-3" />
								{__('Remove', 'allcoach')}
							</button>
						</div>
					)}
				</div>

				{/* Media area */}
				<div className="border-t border-gray-100">
					{!hasMedia ? (
						<button
							type="button"
							onClick={handleOpen}
							className="group/m flex w-full flex-col items-center gap-2 p-8 text-center transition-colors hover:bg-gray-50"
						>
							<Icon className="size-6 text-gray-300 transition-colors group-hover/m:text-teal-400" />
							<div className="text-[13px] font-medium text-gray-500">
								{addLabel}
							</div>
							<div className="text-[11px] text-gray-400">{subLabel}</div>
						</button>
					) : (
						<>
							{libraryType === 'image' && (
								<img
									src={url}
									alt={title}
									className="max-h-64 w-full object-cover"
								/>
							)}
							{libraryType === 'video' && (
								<video src={url} controls className="w-full" />
							)}
							{libraryType === 'audio' && (
								<div className="px-4 py-3">
									<audio src={url} controls className="w-full" />
								</div>
							)}
						</>
					)}
				</div>
			</div>

			{/* Lightbox (image only) */}
			{lightbox &&
				libraryType === 'image' &&
				createPortal(
					<div
						className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 p-8"
						onClick={() => setLightbox(false)}
					>
						<div
							className="relative max-h-[80vh] w-full max-w-3xl overflow-hidden rounded-xl bg-white shadow-2xl"
							onClick={(e) => e.stopPropagation()}
						>
							<button
								type="button"
								onClick={() => setLightbox(false)}
								className="absolute top-3 right-3 z-10 flex size-7 items-center justify-center rounded-full bg-black/40 text-white hover:bg-black/60"
							>
								<X className="size-3.5" />
							</button>
							<img
								src={url}
								alt={title}
								className="max-h-[80vh] w-full object-contain"
							/>
							{title && (
								<div className="border-t border-gray-100 px-4 py-2.5 text-[12px] text-gray-500">
									{title}
								</div>
							)}
						</div>
					</div>,
					document.body,
				)}
		</NodeViewWrapper>
	);
}
