import React, { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { __, sprintf } from '@wordpress/i18n';
import {
	Alert,
	Avatar,
	Button,
	Checkbox,
	Combobox,
	DatePicker,
	Drawer,
	DropdownMenu,
	Field,
	IconButton,
	Icon,
	Input,
	Segmented,
	Select,
	Skeleton,
	SkeletonGroup,
	StatusBadge,
	Textarea,
	TimeSlots,
	useConfirm,
	useDebouncedValue,
	useToast,
} from '../../shared/ui';
import api from '../core/api';
import useAsync from '../core/useAsync';
import useForm from '../core/useForm';
import { useAdmin, useUnsavedChanges } from '../core/AdminContext';
import { LOCALE, adminUrl, formatDuration, formatMoney, formatRange, siteToday, splitDateTime, STATUS_OPTIONS, weekStartsOn } from '../core/format';

/** Renders children into the drawer footer slot (owned by BookingPanel). */
function FooterPortal({ el, children }) {
	return el ? createPortal(children, el) : null;
}

const CORE_CUSTOMER_FIELDS = ['first_name', 'last_name', 'email', 'phone'];

/** Tolerant reader for the availability endpoint ({slots:[...]} possibly nested, items strings or objects). */
function readSlots(res) {
	const payload = res && res.data && res.data.slots ? res.data : res;
	const list = (payload && payload.slots) || (Array.isArray(payload) ? payload : []);
	return list
		.map((s) => (typeof s === 'string' ? s : s && (s.time || s.start_time || s.start || s.label || s.value)) || '')
		.map((t) => String(t).slice(0, 5))
		.filter((t) => /^\d{2}:\d{2}$/.test(t));
}

function CustomFieldInput({ def, value, onChange }) {
	const label = def.label || def.key;
	if (def.type === 'checkbox') {
		return <Checkbox label={label} checked={!!value} onChange={(e) => onChange(e.target.checked)} />;
	}
	let control;
	if (def.type === 'textarea') control = <Textarea rows={3} value={value || ''} onChange={(e) => onChange(e.target.value)} />;
	else if (def.type === 'select') {
		const opts = (Array.isArray(def.options) ? def.options : []).map((o) => (typeof o === 'object' ? { value: o.value ?? o.label, label: o.label ?? o.value } : { value: o, label: o }));
		control = <Select value={value || ''} placeholder={__('Select…', 'pointly-booking')} options={opts} onChange={(e) => onChange(e.target.value)} />;
	} else {
		const type = ['email', 'tel', 'number', 'date'].includes(def.type) ? def.type : 'text';
		control = <Input type={type} value={value || ''} onChange={(e) => onChange(e.target.value)} />;
	}
	return (
		<Field label={label} required={!!def.is_required}>
			{control}
		</Field>
	);
}

/** Search-as-you-type customer picker (uses GET /admin/customers?q=). */
function CustomerPicker({ value, onChange }) {
	const [q, setQ] = useState('');
	const dq = useDebouncedValue(q.trim(), 250);
	const { data, loading } = useAsync(() => api.get('/admin/customers', { q: dq, per: 6 }), [dq]);
	const items = (data && data.items) || [];

	if (value) {
		return (
			<div className="bp-panel-customer">
				<Avatar name={value.name || value.email} size="md" />
				<div className="bp-panel-customer__text">
					<strong>{value.name || value.email}</strong>
					<span className="bp-text-muted">{value.email}</span>
				</div>
				<Button variant="ghost" size="sm" onClick={() => onChange(null)}>
					{__('Change', 'pointly-booking')}
				</Button>
			</div>
		);
	}

	return (
		<div className="bp-panel-picker">
			<Input icon="search" placeholder={__('Search customers by name, email or phone…', 'pointly-booking')} value={q} onChange={(e) => setQ(e.target.value)} aria-label={__('Search customers', 'pointly-booking')} />
			<ul className="bp-panel-picker__list" aria-busy={loading ? 'true' : undefined}>
				{items.map((c) => {
					const name = `${c.first_name || ''} ${c.last_name || ''}`.trim();
					return (
						<li key={c.id}>
							<button type="button" className="bp-panel-picker__item" onClick={() => onChange({ id: Number(c.id), name, email: c.email, phone: c.phone, first_name: c.first_name, last_name: c.last_name })}>
								<Avatar name={name || c.email} size="sm" />
								<span className="bp-panel-picker__text">
									<span>{name || c.email}</span>
									<span className="bp-text-muted bp-text-sm">{c.email}</span>
								</span>
							</button>
						</li>
					);
				})}
				{!loading && items.length === 0 ? <li className="bp-text-muted bp-text-sm bp-panel-picker__empty">{dq ? __('No customers found.', 'pointly-booking') : __('No customers yet.', 'pointly-booking')}</li> : null}
			</ul>
		</div>
	);
}

/** Date + time chooser backed by the availability engine (same rules as customers, minus the booking window). */
function SchedulePicker({ serviceId, agentId, date, time, onDate, onTime, excludeId, originalTime, originalDate }) {
	const ready = !!serviceId && !!date;
	const { data, loading, error } = useAsync(
		() => api.get('/admin/availability/slots', { service_id: serviceId, agent_id: agentId || 0, date, exclude_booking_id: excludeId || undefined }),
		[serviceId, agentId, date, excludeId],
		{ enabled: ready, keepPrevious: false }
	);
	const times = useMemo(() => readSlots(data), [data]);
	// The booking's own time stays selectable while editing, even if the engine hides it.
	const withOwn = useMemo(() => {
		const set = new Set(times);
		if (excludeId && originalTime && date === originalDate) set.add(originalTime);
		return Array.from(set).sort();
	}, [times, excludeId, originalTime, originalDate, date]);

	return (
		<div className="bp-panel-schedule">
			<Field label={__('Date', 'pointly-booking')} required>
				<DatePicker value={date} onChange={onDate} weekStartsOn={weekStartsOn()} locale={LOCALE} />
			</Field>
			<div>
				<div className="bp-label" id="bp-panel-times-label">
					{__('Time', 'pointly-booking')}
				</div>
				{!serviceId ? (
					<p className="bp-text-muted bp-text-sm">{__('Choose a service first to see free times.', 'pointly-booking')}</p>
				) : loading ? (
					<SkeletonGroup label={__('Loading times…', 'pointly-booking')} className="bp-panel-slot-skel">
						{Array.from({ length: 6 }).map((_, i) => (
							<Skeleton key={i} height={38} />
						))}
					</SkeletonGroup>
				) : error ? (
					<Alert tone="danger">{error}</Alert>
				) : withOwn.length === 0 ? (
					<p className="bp-text-muted bp-text-sm">{__('No free times on this day. Pick another date or staff member.', 'pointly-booking')}</p>
				) : (
					<TimeSlots slots={withOwn.map((t) => ({ time: t }))} value={time} onChange={(t) => onTime(t)} format={{ locale: LOCALE }} groupLabelledBy="bp-panel-times-label" />
				)}
			</div>
		</div>
	);
}

/* ------------------------------------------------------------------ create */

function CreateForm({ defaults, onCreated, onClose, footerEl }) {
	const toast = useToast();
	const { notifyBookingsChanged } = useAdmin();
	const services = useAsync(() => api.get('/admin/services'), []);
	const agents = useAsync(() => api.get('/admin/agents'), []);
	const fields = useAsync(() => api.get('/front/form-fields/active'), []);
	const settings = useAsync(() => api.get('/admin/settings'), []);
	const form = useForm({
		serviceId: (defaults && defaults.serviceId) || 0,
		agentId: (defaults && defaults.agentId) || 0,
		date: (defaults && defaults.date) || siteToday(),
		time: (defaults && defaults.time) || '',
		customer: null,
		newCustomer: { first_name: '', last_name: '', email: '', phone: '' },
		customerMode: 'existing',
		notes: '',
		status: '',
		extra: {},
	});
	const v = form.values;
	const [saving, setSaving] = useState(false);
	const [error, setError] = useState('');
	useUnsavedChanges(form.dirty && !saving, 'booking-panel');

	const status = v.status || (settings.data && settings.data.pointlybooking_default_booking_status) || 'pending';
	const serviceList = Array.isArray(services.data) ? services.data : [];
	const agentList = Array.isArray(agents.data) ? agents.data : [];
	const service = serviceList.find((s) => Number(s.id) === Number(v.serviceId));
	const extraDefs = useMemo(() => {
		const d = (fields.data) || {};
		return [...(d.customer || []), ...(d.booking || [])].filter((f) => ![...CORE_CUSTOMER_FIELDS, 'notes'].includes(f.field_key || f.id));
	}, [fields.data]);

	// A time that is no longer offered for the new service/staff/date is dropped.
	const clearTime = (patch) => form.patch({ ...patch, time: '' });

	const save = async () => {
		setError('');
		if (!v.serviceId) return setError(__('Please choose a service.', 'pointly-booking'));
		if (!v.time) return setError(__('Please choose a date and time.', 'pointly-booking'));
		const c = v.customerMode === 'existing' ? v.customer : v.newCustomer;
		if (!c || !(c.email || '').trim()) return setError(__('Please choose a customer or enter an email address.', 'pointly-booking'));
		const customer_fields = { first_name: c.first_name || '', last_name: c.last_name || '', email: c.email, phone: c.phone || '' };
		const booking_fields = { notes: v.notes || '' };
		const field_values = {};
		Object.entries(customer_fields).forEach(([k, val]) => (field_values[`customer.${k}`] = val));
		field_values['booking.notes'] = v.notes || '';
		extraDefs.forEach((d) => {
			const key = d.field_key || d.id;
			const val = v.extra[`${d.scope}.${key}`];
			if (val !== undefined && val !== '') {
				field_values[`${d.scope}.${key}`] = val;
				if (d.scope === 'customer') customer_fields[key] = val;
				else booking_fields[key] = val;
			}
		});
		setSaving(true);
		try {
			const res = await api.post('/admin/bookings', {
				service_id: Number(v.serviceId),
				agent_id: Number(v.agentId) || 0,
				date: v.date,
				start_time: v.time,
				status,
				customer_fields,
				booking_fields,
				field_values,
			});
			toast.success(__('Booking created', 'pointly-booking'));
			notifyBookingsChanged({ id: res.booking_id, created: true });
			onCreated(Number(res.booking_id));
		} catch (e) {
			setError(e.message);
			setSaving(false);
		}
	};

	return (
		<div className="bp-panel-form">
			<FooterPortal el={footerEl}>
				<Button variant="secondary" onClick={onClose} disabled={saving}>
					{__('Cancel', 'pointly-booking')}
				</Button>
				<Button variant="primary" icon="check" onClick={save} loading={saving}>
					{__('Create booking', 'pointly-booking')}
				</Button>
			</FooterPortal>
			{error ? (
				<Alert tone="danger" live>
					{error}
				</Alert>
			) : null}

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Service and staff', 'pointly-booking')}</h3>
				<Field label={__('Service', 'pointly-booking')} required>
					<Combobox
						value={v.serviceId || ''}
						onChange={(val) => clearTime({ serviceId: Number(val) || 0 })}
						options={serviceList.filter((s) => Number(s.is_active ?? 1) === 1 || Number(s.id) === Number(v.serviceId)).map((s) => ({ value: Number(s.id), label: s.name, description: [formatDuration(s.duration_minutes), Number(s.price) > 0 ? formatMoney(s.price, s.currency) : ''].filter(Boolean).join(' · ') }))}
						placeholder={services.loading ? __('Loading…', 'pointly-booking') : __('Choose a service', 'pointly-booking')}
					/>
				</Field>
				<Field label={__('Staff', 'pointly-booking')}>
					<Select
						value={v.agentId || 0}
						onChange={(e) => clearTime({ agentId: Number(e.target.value) || 0 })}
						options={[{ value: 0, label: __('Any available', 'pointly-booking') }, ...agentList.filter((a) => Number(a.is_active ?? 1) === 1).map((a) => ({ value: Number(a.id), label: a.name || `${a.first_name} ${a.last_name}` }))]}
					/>
				</Field>
			</section>

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Date and time', 'pointly-booking')}</h3>
				<SchedulePicker serviceId={v.serviceId} agentId={v.agentId} date={v.date} time={v.time} onDate={(d) => clearTime({ date: d })} onTime={(t) => form.set('time', t)} />
			</section>

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Customer', 'pointly-booking')}</h3>
				<Segmented
					label={__('Customer type', 'pointly-booking')}
					value={v.customerMode}
					onChange={(m) => form.set('customerMode', m)}
					options={[
						{ value: 'existing', label: __('Existing', 'pointly-booking') },
						{ value: 'new', label: __('New customer', 'pointly-booking') },
					]}
				/>
				{v.customerMode === 'existing' ? (
					<CustomerPicker value={v.customer} onChange={(c) => form.set('customer', c)} />
				) : (
					<div className="bp-form-grid bp-form-grid--2">
						<Field label={__('First name', 'pointly-booking')}>
							<Input value={v.newCustomer.first_name} onChange={(e) => form.set('newCustomer', { ...v.newCustomer, first_name: e.target.value })} autoComplete="off" />
						</Field>
						<Field label={__('Last name', 'pointly-booking')}>
							<Input value={v.newCustomer.last_name} onChange={(e) => form.set('newCustomer', { ...v.newCustomer, last_name: e.target.value })} autoComplete="off" />
						</Field>
						<Field label={__('Email', 'pointly-booking')} required>
							<Input type="email" value={v.newCustomer.email} onChange={(e) => form.set('newCustomer', { ...v.newCustomer, email: e.target.value })} autoComplete="off" />
						</Field>
						<Field label={__('Phone', 'pointly-booking')}>
							<Input type="tel" value={v.newCustomer.phone} onChange={(e) => form.set('newCustomer', { ...v.newCustomer, phone: e.target.value })} autoComplete="off" />
						</Field>
					</div>
				)}
			</section>

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Details', 'pointly-booking')}</h3>
				<Field label={__('Status', 'pointly-booking')}>
					<Select value={status} onChange={(e) => form.set('status', e.target.value)} options={STATUS_OPTIONS()} />
				</Field>
				<Field label={__('Notes', 'pointly-booking')}>
					<Textarea rows={3} value={v.notes} onChange={(e) => form.set('notes', e.target.value)} />
				</Field>
				{extraDefs.map((d) => {
					const k = `${d.scope}.${d.field_key || d.id}`;
					return <CustomFieldInput key={k} def={{ ...d, key: d.field_key }} value={v.extra[k]} onChange={(val) => form.set('extra', { ...v.extra, [k]: val })} />;
				})}
				{service ? (
					<p className="bp-text-muted bp-text-sm">
						{sprintf(/* translators: 1: service name, 2: price. */ __('%1$s · %2$s', 'pointly-booking'), service.name, Number(service.price) > 0 ? formatMoney(service.price, service.currency) : __('Free', 'pointly-booking'))}
					</p>
				) : null}
			</section>
		</div>
	);
}

/* --------------------------------------------------------------- view/edit */

function BookingDetails({ id, onClose, footerEl, setHeader }) {
	const toast = useToast();
	const confirm = useConfirm();
	const { notifyBookingsChanged } = useAdmin();
	const { data, loading, error, reload } = useAsync(() => api.get(`/admin/bookings/${id}`), [id], { keepPrevious: false });
	const agents = useAsync(() => api.get('/admin/agents'), []);
	const form = useForm({ status: 'pending', agentId: 0, date: '', time: '', notes: '' });
	const [saving, setSaving] = useState(false);
	const [saveError, setSaveError] = useState('');
	const [rescheduling, setRescheduling] = useState(false);
	const orig = useRef({});
	useUnsavedChanges(form.dirty && !saving, 'booking-panel');

	const booking = (data && data.booking) || null;
	const customer = (data && data.customer) || {};
	const service = (data && data.service) || {};
	const agent = (data && data.agent) || {};
	const pricing = (data && data.pricing) || {};
	const raw = (data && data.raw) || {};

	useEffect(() => {
		if (!booking) return;
		const { date, time } = splitDateTime(booking.start_datetime);
		const next = { status: booking.status || 'pending', agentId: Number(agent.id || raw.agent_id || 0), date, time, notes: raw.admin_notes ?? raw.notes ?? '' };
		orig.current = next;
		form.load(next);
		setRescheduling(false);
		setSaveError('');
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [booking && booking.id, booking && booking.start_datetime, booking && booking.status, raw.agent_id]);

	const v = form.values;
	const agentList = Array.isArray(agents.data) ? agents.data : [];

	const patch = async (changes, okMessage) => {
		setSaving(true);
		setSaveError('');
		try {
			await api.patch(`/admin/bookings/${id}`, changes);
			toast.success(okMessage || __('Booking updated', 'pointly-booking'));
			notifyBookingsChanged({ id });
			await reload();
		} catch (e) {
			setSaveError(e.message);
			toast.error(__('Could not update the booking', 'pointly-booking'), e.message);
		} finally {
			setSaving(false);
		}
	};

	const save = () => {
		const changes = {};
		if (v.status !== orig.current.status) changes.status = v.status;
		if (v.notes !== orig.current.notes) changes.admin_notes = v.notes;
		if (Number(v.agentId) !== Number(orig.current.agentId)) changes.agent_id = Number(v.agentId);
		if (v.date !== orig.current.date || v.time !== orig.current.time || changes.agent_id !== undefined) {
			if (!v.time) return setSaveError(__('Please choose a time.', 'pointly-booking'));
			changes.start_date = v.date;
			changes.start_time = v.time;
		}
		if (!Object.keys(changes).length) return undefined;
		return patch(changes);
	};

	const quickStatus = async (status) => {
		if (status === 'cancelled') {
			const ok = await confirm({ title: __('Cancel this booking?', 'pointly-booking'), message: __('The customer will be notified if cancellation notifications are enabled.', 'pointly-booking'), confirmLabel: __('Cancel booking', 'pointly-booking'), cancelLabel: __('Keep booking', 'pointly-booking'), danger: true });
			if (!ok) return;
		}
		patch({ status }, status === 'confirmed' ? __('Booking confirmed', 'pointly-booking') : status === 'completed' ? __('Marked as completed', 'pointly-booking') : status === 'cancelled' ? __('Booking cancelled', 'pointly-booking') : undefined);
	};

	const remove = async () => {
		const ok = await confirm({ title: __('Delete this booking?', 'pointly-booking'), message: __('This permanently removes the booking. This cannot be undone.', 'pointly-booking'), confirmLabel: __('Delete booking', 'pointly-booking'), danger: true });
		if (!ok) return;
		try {
			await api.del(`/admin/bookings/${id}`);
			toast.success(__('Booking deleted', 'pointly-booking'));
			notifyBookingsChanged({ id, deleted: true });
			onClose(true);
		} catch (e) {
			toast.error(__('Could not delete the booking', 'pointly-booking'), e.message);
		}
	};

	const copyLink = async () => {
		const key = raw.manage_key;
		if (!key) return;
		const url = `${window.location.origin}${(window.pointlybooking_ADMIN && window.pointlybooking_ADMIN.siteUrl ? '' : '')}/?pointlybooking_manage_booking=1&key=${key}`;
		try {
			await navigator.clipboard.writeText(url);
			toast.success(__('Customer link copied', 'pointly-booking'));
		} catch (e) {
			toast.error(__('Could not copy the link', 'pointly-booking'));
		}
	};

	useEffect(() => {
		if (booking) setHeader({ title: sprintf(/* translators: %d: booking number. */ __('Booking #%d', 'pointly-booking'), booking.id), extra: <StatusBadge status={booking.status} /> });
	}, [booking && booking.id, booking && booking.status]); // eslint-disable-line react-hooks/exhaustive-deps

	const footerContent = booking ? (
		<>
				<DropdownMenu
					label={__('More actions', 'pointly-booking')}
					items={[
						...(raw.manage_key ? [{ label: __('Copy customer link', 'pointly-booking'), icon: 'copy', onSelect: copyLink }] : []),
						...(customer.id ? [{ label: __('Open customer', 'pointly-booking'), icon: 'user', href: adminUrl('pointlybooking_customers_edit', { id: customer.id }) }] : []),
						{ separator: true },
						{ label: __('Delete booking', 'pointly-booking'), icon: 'trash', danger: true, onSelect: remove },
					]}
					align="start"
					trigger={({ ref, props }) => (
						<IconButton ref={ref} icon="more-horizontal" label={__('More actions', 'pointly-booking')} variant="ghost" {...props} />
					)}
				/>
				<span className="bp-spacer" />
				<Button variant="secondary" onClick={() => onClose(false)} disabled={saving}>
					{form.dirty ? __('Discard', 'pointly-booking') : __('Close', 'pointly-booking')}
				</Button>
				<Button variant="primary" icon="check" onClick={save} loading={saving} disabled={!form.dirty}>
					{__('Save changes', 'pointly-booking')}
				</Button>
			</>
	) : null;

	if (loading && !booking) {
		return (
			<SkeletonGroup label={__('Loading booking…', 'pointly-booking')} className="bp-panel-form">
				<Skeleton height={90} />
				<Skeleton height={140} />
				<Skeleton height={120} />
			</SkeletonGroup>
		);
	}
	if (error && !booking) {
		return (
			<Alert tone="danger" title={__('Could not load this booking', 'pointly-booking')} actions={<Button size="sm" variant="secondary" onClick={reload}>{__('Try again', 'pointly-booking')}</Button>}>
				{error}
			</Alert>
		);
	}
	if (!booking) return null;

	const answers = data.form_answers || {};
	const answerFields = (data.form_fields || []).filter((f) => f.enabled !== false && !CORE_CUSTOMER_FIELDS.includes(f.key) && f.key !== 'notes' && answers[`${f.scope}.${f.key}`] !== undefined && answers[`${f.scope}.${f.key}`] !== '');
	const durationMin = service.duration_minutes;

	return (
		<div className="bp-panel-form">
			<FooterPortal el={footerEl}>{footerContent}</FooterPortal>
			{saveError ? (
				<Alert tone="danger" live>
					{saveError}
				</Alert>
			) : null}

			<section className="bp-panel-summary">
				<div className="bp-panel-summary__when">
					<Icon name="calendar" />
					<div>
						<strong>{formatRange(booking.start_datetime, booking.end_datetime)}</strong>
						<div className="bp-text-muted bp-text-sm">{[service.name, durationMin ? formatDuration(durationMin) : ''].filter(Boolean).join(' · ')}</div>
					</div>
				</div>
				<div className="bp-panel-quick" role="group" aria-label={__('Change status', 'pointly-booking')}>
					{booking.status === 'pending' ? (
						<Button size="sm" variant="primary" icon="check" onClick={() => quickStatus('confirmed')} disabled={saving}>
							{__('Confirm', 'pointly-booking')}
						</Button>
					) : null}
					{booking.status === 'confirmed' ? (
						<Button size="sm" variant="secondary" icon="check-circle" onClick={() => quickStatus('completed')} disabled={saving}>
							{__('Mark completed', 'pointly-booking')}
						</Button>
					) : null}
					{booking.status !== 'cancelled' ? (
						<Button size="sm" variant="danger-ghost" icon="x" onClick={() => quickStatus('cancelled')} disabled={saving}>
							{__('Cancel', 'pointly-booking')}
						</Button>
					) : (
						<Button size="sm" variant="secondary" icon="rotate-ccw" onClick={() => quickStatus('pending')} disabled={saving}>
							{__('Reopen', 'pointly-booking')}
						</Button>
					)}
				</div>
			</section>

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Customer', 'pointly-booking')}</h3>
				<div className="bp-panel-customer">
					<Avatar name={customer.name || customer.email || '?'} size="md" />
					<div className="bp-panel-customer__text">
						<strong>{customer.name || '—'}</strong>
						{customer.email ? <a href={`mailto:${customer.email}`}>{customer.email}</a> : null}
						{customer.phone ? <a href={`tel:${customer.phone}`} dir="ltr">{customer.phone}</a> : null}
					</div>
				</div>
			</section>

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Appointment', 'pointly-booking')}</h3>
				<div className="bp-form-grid bp-form-grid--2">
					<Field label={__('Status', 'pointly-booking')}>
						<Select value={v.status} onChange={(e) => form.set('status', e.target.value)} options={STATUS_OPTIONS()} />
					</Field>
					<Field label={__('Staff', 'pointly-booking')}>
						<Select value={v.agentId || 0} onChange={(e) => form.patch({ agentId: Number(e.target.value) || 0, time: v.time })} options={[{ value: 0, label: __('Unassigned', 'pointly-booking'), disabled: true }, ...agentList.map((a) => ({ value: Number(a.id), label: a.name || `${a.first_name} ${a.last_name}` }))]} />
					</Field>
				</div>
				{rescheduling ? (
					<SchedulePicker
						serviceId={Number(service.id || raw.service_id || 0)}
						agentId={v.agentId}
						date={v.date}
						time={v.time}
						onDate={(d) => form.patch({ date: d, time: d === orig.current.date ? orig.current.time : '' })}
						onTime={(t) => form.set('time', t)}
						excludeId={id}
						originalTime={orig.current.time}
						originalDate={orig.current.date}
					/>
				) : (
					<Button variant="secondary" size="sm" icon="calendar-plus" onClick={() => setRescheduling(true)}>
						{__('Reschedule', 'pointly-booking')}
					</Button>
				)}
				<Field label={__('Internal notes', 'pointly-booking')} hint={__('Only visible to staff.', 'pointly-booking')}>
					<Textarea rows={3} value={v.notes} onChange={(e) => form.set('notes', e.target.value)} />
				</Field>
			</section>

			<section className="bp-panel-section">
				<h3 className="bp-panel-section__title">{__('Payment', 'pointly-booking')}</h3>
				<dl className="bp-dl">
					<div className="bp-dl__row">
						<dt className="bp-dl__term">{__('Total', 'pointly-booking')}</dt>
						<dd className="bp-dl__desc">{pricing.total !== undefined && pricing.total !== null ? formatMoney(pricing.total, pricing.currency) : '—'}</dd>
					</div>
					{Number(pricing.discount_total) > 0 ? (
						<div className="bp-dl__row">
							<dt className="bp-dl__term">{sprintf(/* translators: %s: promo code. */ __('Discount%s', 'pointly-booking'), pricing.promo_code ? ` (${pricing.promo_code})` : '')}</dt>
							<dd className="bp-dl__desc">−{formatMoney(pricing.discount_total, pricing.currency)}</dd>
						</div>
					) : null}
					<div className="bp-dl__row">
						<dt className="bp-dl__term">{__('Method', 'pointly-booking')}</dt>
						<dd className="bp-dl__desc">{raw.payment_method || '—'}</dd>
					</div>
					<div className="bp-dl__row">
						<dt className="bp-dl__term">{__('Payment status', 'pointly-booking')}</dt>
						<dd className="bp-dl__desc">{raw.payment_status || '—'}</dd>
					</div>
				</dl>
			</section>

			{answerFields.length ? (
				<section className="bp-panel-section">
					<h3 className="bp-panel-section__title">{__('Additional information', 'pointly-booking')}</h3>
					<dl className="bp-dl">
						{answerFields.map((f) => (
							<div className="bp-dl__row" key={`${f.scope}.${f.key}`}>
								<dt className="bp-dl__term">{f.label}</dt>
								<dd className="bp-dl__desc">{String(answers[`${f.scope}.${f.key}`])}</dd>
							</div>
						))}
					</dl>
				</section>
			) : null}
		</div>
	);
}

/* -------------------------------------------------------------------- shell */

export default function BookingPanel({ panel, close }) {
	const confirm = useConfirm();
	const { isDirty, notifyBookingsChanged } = useAdmin();
	const [footerEl, setFooterEl] = useState(null);
	const [header, setHeader] = useState(null);
	const [createdId, setCreatedId] = useState(null);

	// A new panel starts with a clean header/footer.
	useEffect(() => {
		setHeader(null);
		setCreatedId(null);
	}, [panel && panel.id, panel && panel.defaults]);

	if (!panel) return null;

	const id = createdId || panel.id;
	const isCreate = !id;

	const requestClose = async (force) => {
		if (!force && isDirty) {
			const discard = await confirm({
				title: __('Discard your changes?', 'pointly-booking'),
				message: __('You have unsaved changes in this booking.', 'pointly-booking'),
				confirmLabel: __('Discard changes', 'pointly-booking'),
				cancelLabel: __('Keep editing', 'pointly-booking'),
				danger: true,
			});
			if (!discard) return;
		}
		close();
		if (createdId) notifyBookingsChanged({ id: createdId });
	};

	return (
		<Drawer
			open
			onClose={() => requestClose(false)}
			title={header ? header.title : isCreate ? __('New booking', 'pointly-booking') : __('Booking', 'pointly-booking')}
			subtitle={isCreate ? __('Record an appointment for a customer.', 'pointly-booking') : undefined}
			headerExtra={header ? header.extra : null}
			size="lg"
			footer={<div className="bp-panel-footer" ref={setFooterEl} />}
			className="bp-panel"
		>
			{isCreate ? (
				<CreateForm defaults={panel.defaults} onClose={() => requestClose(false)} onCreated={(newId) => setCreatedId(newId)} footerEl={footerEl} />
			) : (
				<BookingDetails key={id} id={id} onClose={(force) => requestClose(force)} footerEl={footerEl} setHeader={setHeader} />
			)}
		</Drawer>
	);
}
