// Customer management is separate from the new-booking wizard and its saved draft.
// The access token stays in the URL fragment and is sent only in the Authorization header.
async function customerBookingRequest(confirmation, token, controller, method = "GET", body) {
	const timeout = setTimeout(() => controller.abort(), 15000);
	try {
		const response = await fetch(`/api/booking/manage/${encodeURIComponent(confirmation)}`, {
			method,
			signal: controller.signal,
			cache: "no-store",
			headers: { Authorization: `Bearer ${token}`, ...(body ? { "Content-Type": "application/json" } : {}) },
			...(body ? { body: JSON.stringify(body) } : {}),
		});
		const result = await response.json();
		if (!response.ok || !result?.ok) {
			const error = new Error(response.status === 404
				? "This private booking link is invalid or no longer available. Open the full link from your confirmation."
				: result?.error || "Your appointment couldn't be updated. Please try again.");
			error.status = response.status;
			throw error;
		}
		if (!result.appointment || result.revision == null)
			throw new Error("Your appointment couldn't be checked. Please try again.");
		return result;
	} finally {
		clearTimeout(timeout);
	}
}

function CustomerBooking({ confirmation, token }) {
	const [data, setData] = useState(null);
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState("");
	const [notice, setNotice] = useState("");
	const [verified, setVerified] = useState(false);
	const [mode, setMode] = useState("overview");
	const [selection, setSelection] = useState({ date: null, time: null, availabilityReady: false });
	const request = useRef(null);
	const mounted = useRef(true);
	const set = useCallback((patch) => setSelection((prev) => ({ ...prev, ...patch })), []);
	const access = !!confirmation && !!token;

	const read = useCallback(async () => {
		if (!access || request.current) return;
		const controller = new AbortController();
		request.current = controller;
		setBusy(true); setError(""); setVerified(false);
		try {
			const result = await customerBookingRequest(confirmation, token, controller);
			if (!mounted.current) return;
			setData(result); setVerified(true);
		} catch (e) {
			if (!mounted.current) return;
			if (e.status === 404) setData(null);
			setError(controller.signal.aborted ? "Your appointment couldn't be loaded in time. Please try again." : e.message);
		} finally {
			request.current = null;
			if (mounted.current) setBusy(false);
		}
	}, [confirmation, token, access]);

	useEffect(() => {
		mounted.current = true;
		read();
		return () => { mounted.current = false; request.current?.abort(); };
	}, [read]);
	useEffect(() => {
		const wrap = document.querySelector(".screen-wrap");
		if (wrap) wrap.scrollTop = 0;
		const title = wrap?.querySelector("h1, h2");
		if (title) { title.tabIndex = -1; title.focus({ preventScroll: true }); }
	}, [mode]);

	const change = async (method) => {
		if (request.current || !verified || !data?.canChange) return;
		const controller = new AbortController();
		request.current = controller;
		setBusy(true); setError(""); setNotice("");
		try {
			const body = { revision: data.revision, ...(method === "PATCH" ? { date: selection.date, time: selection.time } : {}) };
			const result = await customerBookingRequest(confirmation, token, controller, method, body);
			if (!mounted.current) return;
			setData(result); setVerified(true); setMode("overview");
			setNotice(method === "DELETE" ? "Your appointment has been canceled." : "Your new appointment time is confirmed.");
		} catch (e) {
			if (!mounted.current) return;
			setVerified(false); setMode("overview");
			setError(e.status ? e.message : "The response was interrupted. Check the appointment below before making another change.");
			// A mutation may have committed even if its response was lost. Read before another write.
			const recovery = new AbortController();
			request.current = recovery;
			try {
				const current = await customerBookingRequest(confirmation, token, recovery);
				if (!mounted.current) return;
				setData(current); setVerified(true);
				setNotice("The latest saved appointment is shown below.");
			} catch (readError) {
				if (!mounted.current) return;
				if (readError.status === 404) setData(null);
				setError("The current appointment couldn't be verified. Try again before making more changes.");
			}
		} finally {
			request.current = null;
			if (mounted.current) setBusy(false);
		}
	};

	const appointment = data?.appointment;
	const timeZone = data?.timeZone || "America/Chicago";
	const zoneLabel = timeZone === "America/Chicago" ? "Central time (Memphis)" : timeZone.replace(/_/g, " ");
	const cutoff = data?.cutoffAt ? new Intl.DateTimeFormat("en-US", {
		timeZone, weekday: "short", month: "short", day: "numeric", hour: "numeric", minute: "2-digit",
	}).format(new Date(data.cutoffAt)).replace(/\s?AM\b/, " am").replace(/\s?PM\b/, " pm") : null;
	const when = (date, time) => `${prettyDate(date)} · ${fmtTime(time)}`;
	const canChange = verified && data?.canChange && !busy;
	const canceling = mode === "cancel";
	const reviewing = mode === "review";

	return <>
		<header className="topbar">
			{mode !== "overview" ? (
				<button className="icon-btn nav-back" disabled={busy} onClick={() => setMode(reviewing ? "calendar" : "overview")} aria-label="Back">
					<Ico name="back" />
				</button>
			) : <span />}
			<a className="logo-link" href="/" aria-label="Lalaluxe home"><Logo /></a>
			<span className="topbar-right"><ThemeToggle /></span>
		</header>
		<main className="screen-wrap" id="booking-main" aria-busy={busy}>
			{mode === "calendar" ? <CalendarStep cfg={{ schedule: { timeZone } }} s={selection} set={set} dur={appointment.duration} availabilityUrl={`/api/booking/manage/${encodeURIComponent(confirmation)}/availability`} availabilityToken={token} held={{ date: appointment.slotDate, time: appointment.slotTime }} /> :
			<div className="screen manage-screen">
				<header className="screen-head">
					<p className="eyebrow">Your booking</p>
					<h1 className="h-question" tabIndex={-1}>
						{canceling ? <>Cancel your <em>visit?</em></> : reviewing ? <>Your new <em>time.</em></> : <>Your <em>visit.</em></>}
					</h1>
					<p className="h-sub">{canceling ? "Your time will be released after you confirm below." : reviewing ? "Review the change before saving. Your current time is kept until the change is confirmed." : !appointment ? "Your private link opens your visit: when it is, where it is, and how to change it."
						: appointment.status === "canceled" ? "This visit is canceled and its time has been released."
						: appointment.status === "completed" ? "This visit is complete. Thank you for coming in."
						: "Keep the pretty plans. Change the timing when life happens."}</p>
				</header>
				<div className="body">
					{!access && <div className="booking-notice" role="alert">Open the complete private booking link from your confirmation to manage your appointment.</div>}
					{error && <div className="sub-error" role="alert">{error}</div>}
					{notice && <div className="booking-notice" role="status">{notice}</div>}
					{busy && <p className="custom-hint" role="status">{mode === "overview" ? "Checking your appointment…" : "Saving your change…"}</p>}
					{access && !verified && !busy && <button className="cal-retry" onClick={read}>Try again</button>}
					{!appointment && !busy && <a className="cta" href="/">Back to Lalaluxe <Ico name="arrow" /></a>}
					{appointment && <>
						<div className="receipt manage-receipt">
							<div className="rcpt-head"><span className="rcpt-title">{appointment.status === "canceled" ? "Canceled" : appointment.status === "completed" ? "Completed" : "Appointment"}</span><span className="rcpt-date">{confirmation}</span></div>
							<dl className="rcpt-list">
								<div className="rcpt-row"><dt className="l">{reviewing ? "Current time" : "When"}</dt><dd className="r">{when(appointment.slotDate, appointment.slotTime)}</dd></div>
								{reviewing && <div className="rcpt-row manage-new-time"><dt className="l">New time</dt><dd className="r">{when(selection.date, selection.time)}</dd></div>}
								<div className="rcpt-row"><dt className="l">Service</dt><dd className="r">{String(appointment.serviceSummary || "").replace(/ - /g, " · ")}</dd></div>
								{appointment.duration > 0 && <div className="rcpt-row"><dt className="l">Allow about</dt><dd className="r">{humanDuration(appointment.duration)}</dd></div>}
							</dl>
							<div className="rcpt-note">All times are shown in {zoneLabel}.</div>
						</div>
						{mode === "overview" && <>
							{data.studioAddress && <StudioPlace address={data.studioAddress} arrival={data.studioArrival} />}
							{data.canChange ? <p className="custom-hint">Changes close 24 hours before your visit{cutoff ? ` (${cutoff}, ${zoneLabel})` : ""}. Keep this private link for next time.</p> : appointment.status === "booked" && <div className="booking-notice">{data.changeBlockedReason || "Online changes are closed for this visit. Changes must be made more than 24 hours before your appointment."}</div>}
							<div className="manage-actions">
								{data.canChange && <button className="cta" disabled={!canChange} onClick={() => { set({ date: appointment.slotDate, time: null, availabilityReady: false }); setError(""); setNotice(""); setMode("calendar"); }}>Change date or time <Ico name="arrow" /></button>}
								{appointment.status === "canceled" && <a className="cta" href="/">Book a new visit <Ico name="arrow" /></a>}
								<a className="cal-retry" href={`/square-return?confirmation=${encodeURIComponent(confirmation)}#token=${encodeURIComponent(token)}`}>Confirmation &amp; calendar</a>
								{data.canChange && <button className="cal-retry" disabled={!canChange} onClick={() => { setError(""); setNotice(""); setMode("cancel"); }}>Cancel appointment</button>}
							</div>
						</>}
						{canceling && <p className="custom-hint">{data.isPrepaid ? "Your prepaid visit will return to your plan after cancellation. No automatic refund is issued." : data.paymentMode === "bypass" ? "No payment was taken online for this visit, so there is nothing to refund." : "No automatic refund is issued here. The studio handles any refund owed."}</p>}
					</>}
				</div>
			</div>}
		</main>
		<PaintDefs />
		<WallArt />
		{mode !== "overview" && <div className="botbar">
			{mode === "calendar" ? <button className="cta" disabled={!selection.date || !selection.time || !selection.availabilityReady} onClick={() => setMode("review")}>{!selection.date ? "Pick a day" : !selection.time ? "Pick a time" : <>Review change <Ico name="arrow" /></>}</button> :
			<button className={"cta" + (canceling ? " danger" : "")} disabled={!canChange} onClick={() => change(canceling ? "DELETE" : "PATCH")}>{busy ? "Saving…" : canceling ? "Yes, cancel visit" : "Confirm new time"}</button>}
			{canceling && <button className="cta" disabled={busy} onClick={() => setMode("overview")}>Keep visit</button>}
		</div>}
	</>;
}

// The studio's address opens in Maps or copies; how to find the door sits under it.
function StudioPlace({ address, arrival }) {
	const [copied, setCopied] = useState("");
	const line = studioLine(address);
	const href = mapsHref(line);
	const copy = async () => {
		try { await navigator.clipboard.writeText(line); setCopied("Address copied"); }
		catch (_error) { setCopied("Hold the address to copy it"); }
	};
	return <section className="manage-address" aria-label="Your studio">
		<h2 className="co-section">Your studio</h2>
		<a className="manage-address-text" href={href} target="_blank" rel="noopener noreferrer">{address}</a>
		<div className="manage-address-tools">
			<a className="cal-retry" href={href} target="_blank" rel="noopener noreferrer">Open in Maps</a>
			<button type="button" className="cal-retry" onClick={copy}>{copied || "Copy address"}</button>
		</div>
		{arrival && <div className="manage-arrival"><h3 className="co-section">When you arrive</h3><p>{arrival}</p></div>}
	</section>;
}
// The street address on one line, as a maps app or a pasted search wants it. Twinned in confirmation.js.
function studioLine(address) { return String(address || "").split(/\r?\n/).map((part) => part.replace(/\s+/g, " ").trim()).filter(Boolean).join(", "); }
// Apple Maps on an iPhone or iPad (an iPad asks for the desktop site, so it is told by touch), Google elsewhere.
function mapsHref(line) {
	const apple = /iPhone|iPad|iPod/.test(navigator.userAgent) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
	return apple ? `https://maps.apple.com/?q=${encodeURIComponent(line)}` : `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(line)}`;
}
