import { useCallback, useEffect, useRef, useState } from "react";
import { RichTextEditor } from "@platform-modules/ui-editor";
import { jsx, jsxs } from "react/jsx-runtime";
import { defaultConsent, parseConsent } from "@platform-modules/content/privacy";
//#region src/errors.ts
/** Typed boundary error when a workflow action requires a persisted entry id. */
var ContentEntryNotSavedError = class extends Error {
	action;
	name = "ContentEntryNotSavedError";
	constructor(action) {
		super(`cannot ${action}: entry has not been saved yet`);
		this.action = action;
	}
};
function isContentEntryNotSavedError(e) {
	return typeof e === "object" && e !== null && e.name === "ContentEntryNotSavedError";
}
//#endregion
//#region src/stableSerialize.ts
/** Stable JSON key for effect deps — query object identity is not stable across renders. */
function stableSerializeQuery(query) {
	if (!query) return "";
	const keys = Object.keys(query).sort();
	const sorted = {};
	for (const k of keys) sorted[k] = query[k];
	return JSON.stringify(sorted);
}
//#endregion
//#region src/useContentList.ts
function useContentList(client, query) {
	const [entries, setEntries] = useState([]);
	const [loading, setLoading] = useState(true);
	const [error, setError] = useState(null);
	const [reloadToken, setReloadToken] = useState(0);
	const queryKey = stableSerializeQuery(query);
	const clientRef = useRef(client);
	clientRef.current = client;
	const queryRef = useRef(query);
	queryRef.current = query;
	const reload = useCallback(() => {
		setReloadToken((t) => t + 1);
	}, []);
	useEffect(() => {
		let cancelled = false;
		setLoading(true);
		setError(null);
		clientRef.current.list(queryRef.current).then((rows) => {
			if (!cancelled) {
				setEntries(rows);
				setLoading(false);
			}
		}).catch((e) => {
			if (!cancelled) {
				setError(e instanceof Error ? e : new Error(String(e)));
				setLoading(false);
			}
		});
		return () => {
			cancelled = true;
		};
	}, [queryKey, reloadToken]);
	return {
		entries,
		loading,
		error,
		reload
	};
}
//#endregion
//#region src/useContentEntry.ts
function entryToInput(ref) {
	return {
		id: ref.id,
		slug: ref.slug,
		type: ref.type,
		title: ref.title,
		body: ref.body,
		visibility: ref.visibility,
		termIds: ref.terms.map((term) => term.id)
	};
}
function emptyInput() {
	return {
		slug: "",
		type: "",
		title: "",
		body: "",
		visibility: "public"
	};
}
function requireId(entry, action) {
	if (!entry?.id) throw new ContentEntryNotSavedError(action);
	return entry.id;
}
function useContentEntry(client, ref) {
	const [entry, setEntry] = useState(ref);
	const [draft, setDraft] = useState(() => ref ? entryToInput(ref) : emptyInput());
	const [loading, setLoading] = useState(false);
	const [error, setError] = useState(null);
	const clientRef = useRef(client);
	clientRef.current = client;
	useEffect(() => {
		setEntry(ref);
		setDraft(ref ? entryToInput(ref) : emptyInput());
		setError(null);
	}, [ref]);
	const run = useCallback(async (fn) => {
		setLoading(true);
		setError(null);
		try {
			const result = await fn();
			setLoading(false);
			return result;
		} catch (e) {
			const err = e instanceof Error ? e : new Error(String(e));
			setError(err);
			setLoading(false);
			throw err;
		}
	}, []);
	return {
		entry,
		loading,
		error,
		save: useCallback(async (input) => {
			const payload = input ?? draft;
			const saved = await run(() => clientRef.current.put(payload));
			if (saved) {
				setEntry(saved);
				setDraft(entryToInput(saved));
			}
			return saved;
		}, [draft, run]),
		publish: useCallback(async () => {
			return run(async () => {
				const id = requireId(entry, "publish");
				return clientRef.current.publish(id);
			});
		}, [entry, run]),
		schedule: useCallback(async (at) => {
			return run(async () => {
				const id = requireId(entry, "schedule");
				return clientRef.current.schedule(id, at);
			});
		}, [entry, run]),
		unpublish: useCallback(async () => {
			return run(async () => {
				const id = requireId(entry, "unpublish");
				return clientRef.current.unpublish(id);
			});
		}, [entry, run]),
		trash: useCallback(async () => {
			return run(async () => {
				const id = requireId(entry, "trash");
				return clientRef.current.trash(id);
			});
		}, [entry, run]),
		restore: useCallback(async () => {
			return run(async () => {
				const id = requireId(entry, "restore");
				return clientRef.current.restore(id);
			});
		}, [entry, run]),
		setVisibility: useCallback(async (visibility) => {
			const result = await run(async () => {
				const id = requireId(entry, "setVisibility");
				return clientRef.current.setVisibility(id, visibility);
			});
			if (result && entry) setEntry({
				...entry,
				visibility
			});
			return result;
		}, [entry, run]),
		remove: useCallback(async () => {
			return run(async () => {
				const id = requireId(entry, "remove");
				return clientRef.current.remove(id);
			});
		}, [entry, run])
	};
}
//#endregion
//#region src/useAutosave.ts
function useAutosave(save, value, opts) {
	const delayMs = opts?.delayMs ?? 1e3;
	const [state, setState] = useState("idle");
	const saveRef = useRef(save);
	const initialValueRef = useRef(value);
	const mountedRef = useRef(false);
	const timerRef = useRef(null);
	const generationRef = useRef(0);
	useEffect(() => {
		saveRef.current = save;
	});
	useEffect(() => {
		if (!mountedRef.current) {
			mountedRef.current = true;
			initialValueRef.current = value;
			return;
		}
		if (value === initialValueRef.current) return;
		if (timerRef.current !== null) clearTimeout(timerRef.current);
		const generation = ++generationRef.current;
		timerRef.current = setTimeout(() => {
			timerRef.current = null;
			setState("saving");
			saveRef.current(value).then(() => {
				if (generation === generationRef.current) setState("saved");
			}).catch(() => {
				if (generation === generationRef.current) setState("error");
			});
		}, delayMs);
		return () => {
			if (timerRef.current !== null) {
				clearTimeout(timerRef.current);
				timerRef.current = null;
			}
		};
	}, [value, delayMs]);
	useEffect(() => {
		return () => {
			generationRef.current += 1;
			if (timerRef.current !== null) {
				clearTimeout(timerRef.current);
				timerRef.current = null;
			}
		};
	}, []);
	return { state };
}
//#endregion
//#region src/ContentEditorForm.tsx
const VISIBILITY_OPTIONS = [
	"public",
	"private",
	"members"
];
function ContentEditorForm({ client, entry, onSaved, className }) {
	const [title, setTitle] = useState(entry?.title ?? "");
	const [slug, setSlug] = useState(entry?.slug ?? "");
	const [type, setType] = useState(entry?.type ?? "");
	const [visibility, setVisibility] = useState(entry?.visibility ?? "public");
	const [body, setBody] = useState(entry?.body ?? "");
	const [saving, setSaving] = useState(false);
	const [error, setError] = useState(null);
	const handleSave = useCallback(async () => {
		setSaving(true);
		setError(null);
		const input = {
			...entry?.id ? { id: entry.id } : {},
			slug,
			type,
			title,
			body,
			visibility
		};
		try {
			const saved = await client.put(input);
			onSaved?.(saved);
		} catch (e) {
			setError(e instanceof Error ? e : new Error(String(e)));
		} finally {
			setSaving(false);
		}
	}, [
		body,
		client,
		entry?.id,
		onSaved,
		slug,
		title,
		type,
		visibility
	]);
	return /* @__PURE__ */ jsxs("form", {
		className,
		onSubmit: (e) => {
			e.preventDefault();
			handleSave();
		},
		children: [
			/* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs("label", {
				htmlFor: "content-title",
				children: ["Title", /* @__PURE__ */ jsx("input", {
					id: "content-title",
					type: "text",
					value: title,
					onChange: (e) => setTitle(e.target.value)
				})]
			}) }),
			/* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs("label", {
				htmlFor: "content-slug",
				children: ["Slug", /* @__PURE__ */ jsx("input", {
					id: "content-slug",
					type: "text",
					value: slug,
					onChange: (e) => setSlug(e.target.value)
				})]
			}) }),
			/* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs("label", {
				htmlFor: "content-type",
				children: ["Type", /* @__PURE__ */ jsx("input", {
					id: "content-type",
					type: "text",
					value: type,
					onChange: (e) => setType(e.target.value)
				})]
			}) }),
			/* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs("label", {
				htmlFor: "content-visibility",
				children: ["Visibility", /* @__PURE__ */ jsx("select", {
					id: "content-visibility",
					value: visibility,
					onChange: (e) => setVisibility(e.target.value),
					children: VISIBILITY_OPTIONS.map((v) => /* @__PURE__ */ jsx("option", {
						value: v,
						children: v
					}, v))
				})]
			}) }),
			/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("label", { children: ["Body", /* @__PURE__ */ jsx(RichTextEditor, {
				value: body,
				onChange: setBody,
				"aria-label": "Content body"
			})] }) }),
			error ? /* @__PURE__ */ jsx("p", {
				role: "alert",
				children: error.message
			}) : null,
			/* @__PURE__ */ jsx("button", {
				type: "submit",
				disabled: saving,
				children: saving ? "Saving…" : "Save"
			})
		]
	});
}
//#endregion
//#region src/useConsent.ts
const DEFAULT_KEY = "mod-consent";
/**
* Default localStorage-backed storage. LAZY: constructs with no `localStorage` access (safe to evaluate
* as a default during SSR); touches `window.localStorage` only inside load()/save(), each guarded so a
* SecurityError (storage disabled / private mode) degrades to null / no-op, never a throw.
*/
function createLocalStorageConsentStorage(key = DEFAULT_KEY) {
	return {
		load() {
			try {
				return window.localStorage.getItem(key);
			} catch {
				return null;
			}
		},
		save(value) {
			try {
				window.localStorage.setItem(key, value);
			} catch {}
		}
	};
}
/** Read + parse + version-gate. Fail-safe: any throw or mismatch → null (no-consent), never a crash. */
function readStored(storage, version) {
	let raw;
	try {
		raw = storage.load();
	} catch {
		return null;
	}
	if (raw == null) return null;
	let parsed;
	try {
		parsed = JSON.parse(raw);
	} catch {
		return null;
	}
	const state = parseConsent(parsed);
	if (state == null) return null;
	return state.version === version ? state : null;
}
function useConsent(options) {
	const { version } = options;
	const [storage] = useState(() => options.storage ?? createLocalStorageConsentStorage());
	const [ready, setReady] = useState(false);
	const [consent, setConsent] = useState(null);
	useEffect(() => {
		setConsent(readStored(storage, version));
		setReady(true);
	}, [storage, version]);
	const persist = useCallback((categories) => {
		const state = {
			categories: {
				...categories,
				necessary: true
			},
			recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
			version
		};
		storage.save(JSON.stringify(state));
		setConsent(state);
		return state;
	}, [storage, version]);
	return {
		ready,
		consent,
		save: useCallback((categories) => persist(categories), [persist]),
		acceptAll: useCallback(() => persist({
			necessary: true,
			analytics: true,
			marketing: true,
			preferences: true
		}), [persist]),
		rejectAll: useCallback(() => persist({
			necessary: true,
			analytics: false,
			marketing: false,
			preferences: false
		}), [persist])
	};
}
//#endregion
//#region src/ConsentBanner.tsx
const DEFAULT_CATEGORIES = [
	"analytics",
	"marketing",
	"preferences"
];
function safePolicyHref(href) {
	if (href == null) return void 0;
	const cleaned = href.replace(/[\u0000-\u0020]+/g, "");
	const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(cleaned);
	if (!scheme) return href;
	const s = scheme[1].toLowerCase();
	return s === "http" || s === "https" || s === "mailto" ? href : void 0;
}
const DEFAULT_CATEGORY_LABELS = {
	necessary: "Strictly necessary",
	analytics: "Analytics",
	marketing: "Marketing",
	preferences: "Preferences"
};
function ConsentBanner({ version, storage, categories = DEFAULT_CATEGORIES, policyHref, labels, open, onChange, className }) {
	const { ready, consent, save, acceptAll, rejectAll } = useConsent({
		version,
		storage
	});
	const [selection, setSelection] = useState(() => defaultConsent().categories);
	if (!(open ?? (ready && consent == null))) return null;
	const title = labels?.title ?? "Cookie preferences";
	const description = labels?.description ?? "We use cookies to run this site. Choose which categories to allow. Necessary cookies are always on.";
	const catLabel = (c) => labels?.categoryLabels?.[c] ?? DEFAULT_CATEGORY_LABELS[c];
	const href = safePolicyHref(policyHref);
	const handleAcceptAll = () => {
		const state = acceptAll();
		onChange?.(state);
	};
	const handleRejectAll = () => {
		const state = rejectAll();
		onChange?.(state);
	};
	const handleSave = () => {
		const state = save(selection);
		onChange?.(state);
	};
	return /* @__PURE__ */ jsxs("section", {
		role: "region",
		"aria-label": title,
		className,
		children: [
			/* @__PURE__ */ jsx("h2", { children: title }),
			/* @__PURE__ */ jsx("p", { children: description }),
			href ? /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", {
				href,
				children: labels?.policyLink ?? "Privacy policy"
			}) }) : null,
			/* @__PURE__ */ jsxs("fieldset", { children: [
				/* @__PURE__ */ jsx("legend", { children: "Cookie categories" }),
				/* @__PURE__ */ jsxs("label", { children: [/* @__PURE__ */ jsx("input", {
					type: "checkbox",
					checked: true,
					disabled: true,
					"aria-label": catLabel("necessary"),
					readOnly: true
				}), catLabel("necessary")] }),
				categories.map((c) => /* @__PURE__ */ jsxs("label", { children: [/* @__PURE__ */ jsx("input", {
					type: "checkbox",
					"aria-label": catLabel(c),
					checked: selection[c] ?? false,
					onChange: (e) => setSelection((s) => ({
						...s,
						[c]: e.target.checked
					}))
				}), catLabel(c)] }, c))
			] }),
			/* @__PURE__ */ jsxs("div", { children: [
				/* @__PURE__ */ jsx("button", {
					type: "button",
					onClick: handleAcceptAll,
					children: labels?.acceptAll ?? "Accept all"
				}),
				/* @__PURE__ */ jsx("button", {
					type: "button",
					onClick: handleRejectAll,
					children: labels?.rejectAll ?? "Reject all"
				}),
				/* @__PURE__ */ jsx("button", {
					type: "button",
					onClick: handleSave,
					children: labels?.save ?? "Save preferences"
				})
			] })
		]
	});
}
//#endregion
export { ConsentBanner, ContentEditorForm, ContentEntryNotSavedError, createLocalStorageConsentStorage, isContentEntryNotSavedError, useAutosave, useConsent, useContentEntry, useContentList };
