import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { useConfirm } from '../../shared/ui';
import { bpEmit, bpOn } from '../lib/bpEvents';

const Ctx = createContext(null);

/**
 * App-wide state that screens and the shell share:
 *  - unsaved-changes guard (`useUnsavedChanges`), enforced on reload/close and on in-app links;
 *  - the booking side panel, openable from anywhere (`openBooking`);
 *  - a bookings-changed event so lists and the calendar refresh after a save.
 */
export function AdminProvider({ children, renderBookingPanel }) {
	const confirm = useConfirm();
	const dirtyRef = useRef(new Set());
	const [dirtyCount, setDirtyCount] = useState(0);
	const [panel, setPanel] = useState(null); // { id?, defaults? } | null

	const setDirty = useCallback((key, isDirty) => {
		const set = dirtyRef.current;
		const had = set.has(key);
		if (isDirty && !had) set.add(key);
		else if (!isDirty && had) set.delete(key);
		else return;
		setDirtyCount(set.size);
	}, []);

	const isDirty = dirtyCount > 0;

	// Browser-level guard: closing the tab or reloading.
	useEffect(() => {
		if (!isDirty) return undefined;
		const handler = (e) => {
			e.preventDefault();
			e.returnValue = '';
		};
		window.addEventListener('beforeunload', handler);
		return () => window.removeEventListener('beforeunload', handler);
	}, [isDirty]);

	// In-app guard: every normal link click inside the admin app asks first.
	useEffect(() => {
		if (!isDirty) return undefined;
		const onClick = async (e) => {
			const a = e.target instanceof Element ? e.target.closest('a[href]') : null;
			if (!a || a.target === '_blank' || e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || a.hasAttribute('download')) return;
			const href = a.getAttribute('href') || '';
			if (href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return;
			e.preventDefault();
			e.stopPropagation();
			const leave = await confirm({
				title: __('Leave without saving?', 'pointly-booking'),
				message: __('You have unsaved changes. If you leave this page they will be lost.', 'pointly-booking'),
				confirmLabel: __('Leave page', 'pointly-booking'),
				cancelLabel: __('Stay', 'pointly-booking'),
				danger: true,
			});
			if (leave) {
				dirtyRef.current.clear();
				window.location.href = a.href;
			}
		};
		document.addEventListener('click', onClick, true);
		return () => document.removeEventListener('click', onClick, true);
	}, [isDirty, confirm]);

	const openBooking = useCallback((opts = {}) => setPanel({ id: opts.id || null, defaults: opts.defaults || null }), []);
	const closeBooking = useCallback(() => setPanel(null), []);
	const notifyBookingsChanged = useCallback((detail) => bpEmit('bookings-changed', detail), []);

	const value = useMemo(
		() => ({ setDirty, isDirty, openBooking, closeBooking, notifyBookingsChanged, panel }),
		[setDirty, isDirty, openBooking, closeBooking, notifyBookingsChanged, panel]
	);

	return (
		<Ctx.Provider value={value}>
			{children}
			{renderBookingPanel ? renderBookingPanel({ panel, close: closeBooking }) : null}
		</Ctx.Provider>
	);
}

const fallback = { setDirty() {}, isDirty: false, openBooking() {}, closeBooking() {}, notifyBookingsChanged() {}, panel: null };
export const useAdmin = () => useContext(Ctx) || fallback;

/** Marks the current screen as having unsaved changes while `dirty` is true. */
export function useUnsavedChanges(dirty, key = 'screen') {
	const { setDirty } = useAdmin();
	useEffect(() => {
		setDirty(key, !!dirty);
		return () => setDirty(key, false);
	}, [dirty, key, setDirty]);
}

/** Runs `fn` whenever bookings are created/changed anywhere in the app. */
export function useOnBookingsChanged(fn) {
	const ref = useRef(fn);
	ref.current = fn;
	useEffect(() => bpOn('bookings-changed', (d) => ref.current(d)), []);
}
