import IconSelector_default from "../../../components/IconSelector.vue.js";
import Callout_default from "../../components/callout/Callout.vue.js";
import { downloadAsFile } from "../../helpers/download-document.js";
import LabelInput_default from "./components/LabelInput.vue.js";
import SyncConflictResolutionEditor_default from "./components/SyncConflictResolutionEditor.vue.js";
import Tabs_default from "./components/Tabs.vue.js";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeProps, openBlock, ref, resolveDynamicComponent, unref, withCtx } from "vue";
import { ScalarButton, ScalarModal, useModal } from "@scalar/components";
import { useToasts } from "@scalar/use-toasts";
import { RouterView } from "vue-router";
import { LibraryIcon } from "@scalar/icons/library";
import { ScalarIconCloudArrowDown, ScalarIconDownload, ScalarIconFloppyDisk, ScalarIconSpinner, ScalarIconWarning } from "@scalar/icons";
import { deepClone } from "@scalar/workspace-store/helpers/deep-clone";
import { apply } from "@scalar/json-magic/diff";
//#region src/v2/features/collection/DocumentCollection.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = { class: "custom-scroll h-full" };
var _hoisted_2 = {
	key: 0,
	class: "w-full px-3 md:mx-auto md:max-w-180"
};
var _hoisted_3 = ["aria-label"];
var _hoisted_4 = { class: "flex flex-row items-center justify-between gap-2" };
var _hoisted_5 = { class: "flex min-w-0 items-center gap-2" };
var _hoisted_6 = { class: "group relative ml-1.25 min-w-0" };
var _hoisted_7 = { class: "px-1.5 py-8" };
var _hoisted_8 = {
	key: 1,
	class: "flex w-full flex-1 items-center justify-center"
};
var _hoisted_9 = { class: "flex flex-col gap-5" };
var _hoisted_10 = { class: "flex gap-3" };
var _hoisted_11 = {
	"aria-hidden": "true",
	class: "bg-b-3 text-c-2 flex size-10 shrink-0 items-center justify-center rounded-lg"
};
var _hoisted_12 = { class: "flex flex-wrap items-center justify-end gap-2 border-t border-[var(--scalar-border-color)] pt-4" };
var _hoisted_13 = { class: "flex h-full w-full flex-col gap-4 overflow-hidden" };
var DocumentCollection_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
	name: "DocumentCollection",
	props: {
		documentSlug: {},
		document: {},
		eventBus: {},
		layout: {},
		path: {},
		method: {},
		exampleName: {},
		environment: {},
		securitySchemes: {},
		workspaceStore: {},
		activeWorkspace: {},
		plugins: {},
		customThemes: {},
		currentTheme: {},
		isDarkMode: { type: Boolean },
		fetchRegistryDocument: { type: Function },
		telemetry: { type: Boolean },
		onUpdateTelemetry: { type: Function }
	},
	setup(__props) {
		const props = __props;
		/** Snag the title from the info object */
		const title = computed(() => props.document?.info?.title ?? "");
		/** Default to the folder icon */
		const icon = computed(() => props.document?.["x-scalar-icon"] || "interface-content-folder");
		const syncModal = useModal();
		const dirtyBeforeSyncModal = useModal();
		const isDocumentDirty = computed(() => props.document?.["x-scalar-is-dirty"] === true);
		const documentSourceUrl = computed(() => props.document?.["x-scalar-original-source-url"]);
		const documentRegistryMeta = computed(() => props.document?.["x-scalar-registry-meta"]);
		/** Show Sync when the document has a source URL or registry meta (registry can be used if fetchRegistryDocument is set). */
		const canShowSyncButton = computed(() => documentSourceUrl.value !== void 0 || documentRegistryMeta.value !== void 0);
		const { toast } = useToasts();
		const undoChanges = () => {
			props.workspaceStore.revertDocumentChanges(props.documentSlug);
		};
		const saveChanges = () => {
			props.workspaceStore.saveDocument(props.documentSlug);
		};
		/** Downloads the document as a JSON file using the last saved state. */
		const downloadDocument = () => {
			const content = props.workspaceStore.exportDocument(props.documentSlug, "json", false);
			if (!content) return;
			downloadAsFile(content, `${title.value.replace(/[^\w\s-]/g, "").trim() || "document"}.json`);
		};
		const handleSaveThenCloseDirtyModal = async () => {
			await props.workspaceStore.saveDocument(props.documentSlug);
			dirtyBeforeSyncModal.hide();
			await handleSyncFlow();
		};
		const handleDiscardThenCloseDirtyModal = async () => {
			await props.workspaceStore.revertDocumentChanges(props.documentSlug);
			dirtyBeforeSyncModal.hide();
			await handleSyncFlow();
		};
		const isSyncInProgress = ref(false);
		const rebaseResult = ref(null);
		/**
		* Resolves the source for syncing. Registry meta has priority over x-scalar-original-source-url
		* when fetchRegistryDocument is provided. Returns either a URL or the full document object.
		*/
		const resolveSyncInput = async () => {
			const registryMeta = documentRegistryMeta.value;
			if (registryMeta && props.fetchRegistryDocument) try {
				const result = await props.fetchRegistryDocument(registryMeta);
				if (!result.ok) {
					toast(result.error, "error");
					return null;
				}
				return { document: result.data };
			} catch (err) {
				toast("Failed to resolve document from registry", "error");
				return null;
			}
			const url = documentSourceUrl.value;
			if (url) return { url };
			return null;
		};
		/**
		* Handles actions to perform when synchronization is complete.
		* Hides the sync modal, resets the sync progress flag, and emits the
		* 'hooks:on:rebase:document:complete' event with document metadata.
		*/
		const onSyncComplete = () => {
			syncModal.hide();
			isSyncInProgress.value = false;
			toast("Your document has been rebased with the latest version from the source.", "info");
			props.eventBus.emit("hooks:on:rebase:document:complete", { meta: { documentName: props.documentSlug } });
		};
		/**
		* Handles errors that occur during synchronization.
		* If an error string is provided, it displays the error via toast.
		* Always resets the sync progress flag.
		*/
		const onSyncError = (error) => {
			if (error !== null) toast(error, "error");
			isSyncInProgress.value = false;
		};
		/**
		* Handles the synchronization flow for a document.
		* Checks for unsaved changes, resolves source (registry over URL),
		* initiates rebasing, handles conflicts, and emits completion events.
		* If conflicts are detected, a modal dialog is shown for user resolution.
		*/
		const handleSyncFlow = async () => {
			if (isDocumentDirty.value) {
				dirtyBeforeSyncModal.show();
				return;
			}
			if (isSyncInProgress.value) return;
			isSyncInProgress.value = true;
			const input = await resolveSyncInput();
			if (!input) {
				onSyncError(null);
				return;
			}
			const result = await props.workspaceStore.rebaseDocument({
				name: props.documentSlug,
				...input
			});
			if (result?.ok) {
				const originalDocument = props.workspaceStore.getOriginalDocument(props.documentSlug) ?? {};
				rebaseResult.value = {
					conflicts: result.conflicts,
					applyChanges: result.applyChanges,
					resolvedDocument: apply(deepClone(originalDocument), result.changes),
					originalDocument
				};
				if (rebaseResult.value.conflicts.length > 0) syncModal.show();
				else {
					await rebaseResult.value?.applyChanges({ resolvedDocument: rebaseResult.value.resolvedDocument });
					onSyncComplete();
				}
			} else if (result?.ok === false && result.type === "NO_CHANGES_DETECTED") onSyncComplete();
			else onSyncError("Failed to sync document");
		};
		const handleApplyChanges = async ({ resolvedDocument }) => {
			await rebaseResult.value?.applyChanges({ resolvedDocument });
			props.eventBus.emit("hooks:on:rebase:document:complete", { meta: { documentName: props.documentSlug } });
			syncModal.hide();
		};
		/**
		* Resets sync state when the sync conflict modal is closed (dismissed or after
		* applying changes). Ensures the Sync button is re-enabled and conflict state
		* is cleared.
		*/
		const onSyncModalClose = () => {
			isSyncInProgress.value = false;
			rebaseResult.value = null;
		};
		return (_ctx, _cache) => {
			return openBlock(), createElementBlock(Fragment, null, [
				createElementVNode("div", _hoisted_1, [__props.document ? (openBlock(), createElementBlock("div", _hoisted_2, [
					createElementVNode("div", {
						"aria-label": `title: ${title.value}`,
						class: "mx-auto flex h-fit w-full flex-col gap-2 pt-14 pb-3 md:max-w-180 md:pt-6"
					}, [__props.document?.["x-scalar-is-dirty"] ? (openBlock(), createBlock(Callout_default, {
						key: 0,
						class: "mb-5",
						type: "warning"
					}, {
						actions: withCtx(() => [createVNode(unref(ScalarButton), {
							class: "text-c-2 hover:text-c-1 flex items-center gap-2",
							size: "xs",
							type: "button",
							variant: "outlined",
							onClick: undoChanges
						}, {
							default: withCtx(() => [..._cache[5] || (_cache[5] = [createElementVNode("span", null, "Undo", -1)])]),
							_: 1
						}), createVNode(unref(ScalarButton), {
							class: "text-c-btn flex items-center gap-2",
							size: "xs",
							type: "button",
							variant: "solid",
							onClick: saveChanges
						}, {
							default: withCtx(() => [createVNode(unref(ScalarIconFloppyDisk), {
								size: "sm",
								thickness: "1.5"
							}), _cache[6] || (_cache[6] = createElementVNode("span", null, "Save", -1))]),
							_: 1
						})]),
						default: withCtx(() => [_cache[7] || (_cache[7] = createElementVNode("p", null, " You have unsaved changes. Save your work to keep your changes, or undo to revert them. ", -1))]),
						_: 1
					})) : createCommentVNode("", true), createElementVNode("div", _hoisted_4, [
						createElementVNode("div", _hoisted_5, [createVNode(IconSelector_default, {
							modelValue: icon.value,
							placement: "bottom-start",
							"onUpdate:modelValue": _cache[0] || (_cache[0] = (icon) => __props.eventBus.emit("document:update:icon", icon))
						}, {
							default: withCtx(() => [createVNode(unref(ScalarButton), {
								class: "hover:bg-b-2 aspect-square h-7 w-7 cursor-pointer rounded border border-transparent p-0 hover:border-inherit",
								variant: "ghost"
							}, {
								default: withCtx(() => [createVNode(unref(LibraryIcon), {
									class: "text-c-2 size-5",
									src: icon.value,
									"stroke-width": "2"
								}, null, 8, ["src"])]),
								_: 1
							})]),
							_: 1
						}, 8, ["modelValue"]), createElementVNode("div", _hoisted_6, [createVNode(LabelInput_default, {
							class: "text-xl font-bold",
							inputId: "documentName",
							modelValue: title.value,
							"onUpdate:modelValue": _cache[1] || (_cache[1] = (title) => __props.eventBus.emit("document:update:info", { title }))
						}, null, 8, ["modelValue"])])]),
						createVNode(unref(ScalarButton), {
							class: "text-c-2 hover:text-c-1 flex shrink-0 items-center gap-2",
							size: "xs",
							type: "button",
							variant: "ghost",
							onClick: downloadDocument
						}, {
							default: withCtx(() => [createVNode(unref(ScalarIconDownload), {
								size: "sm",
								thickness: "1.5"
							}), _cache[8] || (_cache[8] = createElementVNode("span", null, "Download document", -1))]),
							_: 1
						}),
						canShowSyncButton.value ? (openBlock(), createBlock(unref(ScalarButton), {
							key: 0,
							class: "text-c-2 hover:text-c-1 shrink-0 gap-1.5",
							"data-testid": "document-sync-button",
							disabled: isSyncInProgress.value,
							size: "xs",
							title: "Pull the latest version from the document source and merge with your local copy. Save your changes first if you have unsaved edits.",
							type: "button",
							variant: "ghost",
							onClick: handleSyncFlow
						}, {
							default: withCtx(() => [isSyncInProgress.value ? (openBlock(), createBlock(unref(ScalarIconSpinner), {
								key: 0,
								class: "size-3.5 animate-spin",
								size: "sm"
							})) : (openBlock(), createBlock(unref(ScalarIconCloudArrowDown), {
								key: 1,
								class: "size-3.5",
								size: "sm",
								thickness: "1.5"
							})), _cache[9] || (_cache[9] = createElementVNode("span", null, "Sync from source", -1))]),
							_: 1
						}, 8, ["disabled"])) : createCommentVNode("", true)
					])], 8, _hoisted_3),
					createVNode(Tabs_default, { type: "document" }),
					createElementVNode("div", _hoisted_7, [createVNode(unref(RouterView), null, {
						default: withCtx(({ Component }) => [(openBlock(), createBlock(resolveDynamicComponent(Component), mergeProps(props, { collectionType: "document" }), null, 16))]),
						_: 1
					})])
				])) : (openBlock(), createElementBlock("div", _hoisted_8, [..._cache[10] || (_cache[10] = [createElementVNode("div", { class: "flex h-full flex-col items-center justify-center" }, [createElementVNode("h1", { class: "text-2xl font-bold" }, "Document not found"), createElementVNode("p", { class: "text-gray-500" }, " The document you are looking for does not exist. ")], -1)])]))]),
				createVNode(unref(ScalarModal), {
					bodyClass: "border-t-0 rounded-t-lg flex flex-col gap-5",
					size: "xs",
					state: unref(dirtyBeforeSyncModal),
					title: "Sync requires saved document",
					onClose: _cache[3] || (_cache[3] = ($event) => unref(dirtyBeforeSyncModal).hide())
				}, {
					default: withCtx(() => [createElementVNode("div", _hoisted_9, [createElementVNode("div", _hoisted_10, [createElementVNode("div", _hoisted_11, [createVNode(unref(ScalarIconWarning), { class: "size-5 text-[var(--scalar-color-yellow)]" })]), _cache[11] || (_cache[11] = createElementVNode("div", { class: "min-w-0 flex-1 space-y-1" }, [createElementVNode("p", { class: "text-c-1 text-sm leading-snug font-medium" }, " You have unsaved changes "), createElementVNode("p", { class: "text-c-2 text-sm leading-relaxed" }, " Save your work to keep changes, or discard to revert to the last saved version. Then you can sync with the source. ")], -1))]), createElementVNode("div", _hoisted_12, [
						createVNode(unref(ScalarButton), {
							size: "sm",
							type: "button",
							variant: "ghost",
							onClick: _cache[2] || (_cache[2] = ($event) => unref(dirtyBeforeSyncModal).hide())
						}, {
							default: withCtx(() => [..._cache[12] || (_cache[12] = [createTextVNode(" Cancel ", -1)])]),
							_: 1
						}),
						createVNode(unref(ScalarButton), {
							size: "sm",
							type: "button",
							variant: "outlined",
							onClick: handleDiscardThenCloseDirtyModal
						}, {
							default: withCtx(() => [..._cache[13] || (_cache[13] = [createTextVNode(" Discard changes ", -1)])]),
							_: 1
						}),
						createVNode(unref(ScalarButton), {
							class: "flex items-center gap-2",
							size: "sm",
							type: "button",
							variant: "solid",
							onClick: handleSaveThenCloseDirtyModal
						}, {
							default: withCtx(() => [createVNode(unref(ScalarIconFloppyDisk), {
								size: "sm",
								thickness: "1.5"
							}), _cache[14] || (_cache[14] = createTextVNode(" Save and continue ", -1))]),
							_: 1
						})
					])])]),
					_: 1
				}, 8, ["state"]),
				rebaseResult.value ? (openBlock(), createBlock(unref(ScalarModal), {
					key: 0,
					bodyClass: "sync-conflict-modal-root flex h-dvh flex-col p-4",
					maxWidth: "calc(100dvw - 32px)",
					size: "full",
					state: unref(syncModal),
					onClose: onSyncModalClose
				}, {
					default: withCtx(() => [createElementVNode("div", _hoisted_13, [createVNode(SyncConflictResolutionEditor_default, {
						baseDocument: rebaseResult.value.originalDocument,
						conflicts: rebaseResult.value.conflicts,
						resolvedDocument: rebaseResult.value.resolvedDocument,
						onApplyChanges: _cache[4] || (_cache[4] = (payload) => handleApplyChanges(payload))
					}, null, 8, [
						"baseDocument",
						"conflicts",
						"resolvedDocument"
					])])]),
					_: 1
				}, 8, ["state"])) : createCommentVNode("", true)
			], 64);
		};
	}
});
//#endregion
export { DocumentCollection_vue_vue_type_script_setup_true_lang_default as default };

//# sourceMappingURL=DocumentCollection.vue.script.js.map