/**
 * Installation Manager for MPV.Rocks Installer
 * Handles installation and uninstallation progress with SSE streaming and UI updates
 */

(function () {
	"use strict";

	// Installation state
	let activeSessionId = null;
	let eventSource = null;

	/**
	 * Initialize event delegation for install/uninstall buttons
	 * Uses event delegation to handle dynamically added content
	 */
	function initInstallHandlers() {
		// Add delegated click listener (idempotent - only adds once)
		if (!window._installHandlersInitialized) {
			document.body.addEventListener("click", handleDelegatedClick);
			window._installHandlersInitialized = true;
			console.log(
				"[Install] Initialized event delegation for install/uninstall buttons",
			);
		}
	}

	/**
	 * Delegated click handler - routes to appropriate handler based on target
	 */
	function handleDelegatedClick(e) {
		// Check for install button click
		const installButton = e.target.closest("[data-install]");
		if (installButton) {
			e.preventDefault();
			handleInstallClick(installButton);
			return;
		}

		// Check for uninstall button click
		const uninstallButton = e.target.closest("[data-uninstall]");
		if (uninstallButton) {
			e.preventDefault();
			e.stopPropagation();
			handleUninstallClick(uninstallButton);
			return;
		}

		// Check for create shortcut button click
		const createShortcutButton = e.target.closest("[data-create-shortcut]");
		if (createShortcutButton) {
			e.preventDefault();
			handleCreateShortcutClick(createShortcutButton);
			return;
		}

		// Check for file association setup button click
		const fileAssocSetupButton = e.target.closest("[data-file-assoc-setup]");
		if (fileAssocSetupButton) {
			e.preventDefault();
			handleFileAssocSetupClick(fileAssocSetupButton);
			return;
		}

		// Check for file association remove button click
		const fileAssocRemoveButton = e.target.closest("[data-file-assoc-remove]");
		if (fileAssocRemoveButton) {
			e.preventDefault();
			handleFileAssocRemoveClick(fileAssocRemoveButton);
			return;
		}
	}

	/**
	 * Handle create shortcut button click
	 */
	async function handleCreateShortcutClick(button) {
		const appName = button.dataset.appName;

		// Disable button and show loading state
		button.disabled = true;
		button.dataset.originalText = button.innerHTML;
		button.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i> Creating...';

		try {
			const response = await fetch("/api/shortcuts/create", {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
			});

			const data = await response.json();

			if (!response.ok) {
				throw new Error(
					data.message || `HTTP error! status: ${response.status}`,
				);
			}

			console.log("[Install] Shortcuts created successfully");

			// Show success toast
			if (window.showToast) {
				window.showToast(
					"Desktop and Start Menu shortcuts created successfully!",
					"success",
				);
			}
		} catch (error) {
			console.error("[Install] Error creating shortcuts:", error);

			// Show error toast
			if (window.showToast) {
				window.showToast(
					error.message || "Failed to create shortcuts",
					"error",
				);
			}
		} finally {
			// Re-enable button
			button.disabled = false;
			button.innerHTML =
				button.dataset.originalText ||
				'<i class="bx bx-link"></i> Create Shortcut';
		}
	}

	/**
	 * Handle file association setup button click
	 */
	async function handleFileAssocSetupClick(button) {
		// Disable button and show loading state
		button.disabled = true;
		button.dataset.originalText = button.innerHTML;
		button.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i> Setting up...';

		try {
			const response = await fetch("/api/file-associations/setup", {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
			});

			const data = await response.json();

			if (!response.ok) {
				throw new Error(
					data.message || data.error || `HTTP error! status: ${response.status}`,
				);
			}

			console.log("[Install] File associations set up successfully");

			// Show success toast
			if (window.showToast) {
				window.showToast(
					data.message || "File associations registered successfully!",
					"success",
				);
			}
		} catch (error) {
			console.error("[Install] Error setting up file associations:", error);

			// Show error toast
			if (window.showToast) {
				window.showToast(
					error.message || "Failed to set up file associations",
					"error",
				);
			}
		} finally {
			// Re-enable button
			button.disabled = false;
			button.innerHTML =
				button.dataset.originalText ||
				'<i class="bx bx-link"></i> Install File Associations';
		}
	}

	/**
	 * Handle file association remove button click
	 */
	async function handleFileAssocRemoveClick(button) {
		// Show confirmation using modal system
		const doRemove = async () => {
			// Disable button and show loading state
			button.disabled = true;
			button.dataset.originalText = button.innerHTML;
			button.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i> Removing...';

			try {
				const response = await fetch("/api/file-associations/remove", {
					method: "POST",
					headers: {
						"Content-Type": "application/json",
					},
				});

				const data = await response.json();

				if (!response.ok) {
					throw new Error(
						data.message || data.error || `HTTP error! status: ${response.status}`,
					);
				}

				console.log("[Install] File associations removed successfully");

				// Show success toast
				if (window.showToast) {
					window.showToast(
						data.message || "File associations removed successfully!",
						"success",
					);
				}
			} catch (error) {
				console.error("[Install] Error removing file associations:", error);

				// Show error toast
				if (window.showToast) {
					window.showToast(
						error.message || "Failed to remove file associations",
						"error",
					);
				}
			} finally {
				// Re-enable button
				button.disabled = false;
				button.innerHTML =
					button.dataset.originalText ||
					'<i class="bx bx-unlink"></i> Remove File Associations';
			}
		};

		// Use modal confirmation if available
		if (window.MPVRocksModal && window.MPVRocksModal.confirm) {
			window.MPVRocksModal.confirm(
				"Remove File Associations?",
				"Are you sure you want to remove MPV file associations? This will revert the default player settings for video files.",
				"Remove",
				"Cancel",
				doRemove,
				true, // isDanger = true (red button)
			);
		} else {
			// Fallback to browser confirm
			if (confirm("Remove MPV file associations?")) {
				doRemove();
			}
		}
	}

	/**
	 * Handle install button click
	 */
	async function handleInstallClick(button) {
		// Prevent double-click / rapid clicks
		if (button.disabled) {
			console.log("[Install] Button already disabled, ignoring click");
			return;
		}

		// Check if there's already an active job for this method
		if (window.jobManager && window.jobManager.activeJobs) {
			const methodId = button.dataset.methodId;
			const activeJobs = Array.from(window.jobManager.activeJobs.values());
			const existingJob = activeJobs.find(
				(job) =>
					job.methodId === methodId &&
					(job.status === "running" || job.status === "pending"),
			);
			if (existingJob) {
				console.log("[Install] Job already running for method:", methodId);
				if (window.showToast) {
					window.showToast(
						'Installation already in progress. Click "View Progress" to see details.',
						"info",
						4000,
					);
				}
				// Show the existing job output
				if (window.showJobOutput) {
					window.showJobOutput(existingJob.id);
				}
				return;
			}
		}

		// Get method ID
		const methodId = button.dataset.methodId;
		if (!methodId) {
			console.error("[Install] Missing method_id on button");
			showError("Missing installation method ID");
			return;
		}

		// Check if this is an update
		const isUpdate = button.dataset.isUpdate === "true";

		// Check if this method requires sudo and we need to prompt for password
		const requiresSudo = button.dataset.requiresSudo === "true";
		const methodType = button.dataset.methodType || "unknown";
		console.log("[Install] Debug - methodId:", methodId, "methodType:", methodType, "requiresSudo:", requiresSudo, "PasswordModal exists:", !!window.PasswordModal);

		if (requiresSudo && window.PasswordModal) {
			console.log("[Install] Sudo required, calling PasswordModal.ensurePassword");
			// Ensure password is available before proceeding
			const hasPassword = await window.PasswordModal.ensurePassword(methodId, () => {
				// This callback is executed after password is confirmed
				console.log("[Install] Password confirmed callback, proceeding with install");
				proceedWithInstall(button, methodId, isUpdate);
			});
			
			// If modal was shown (no password stored), return and wait for callback
			if (!hasPassword) {
				console.log("[Install] Modal shown, waiting for user input");
				return;
			}
			console.log("[Install] Password already stored, proceeding");
		} else if (requiresSudo && !window.PasswordModal) {
			console.error("[Install] Sudo required but PasswordModal not available!");
			// Show error to user
			if (window.showToast) {
				window.showToast(
					"Password authentication not available. Please use TUI mode or ensure scripts are loaded.",
					"error",
					5000
				);
			}
			return;
		}

		// Proceed with installation
		console.log("[Install] Proceeding with installation (no sudo required or password available)");
		proceedWithInstall(button, methodId, isUpdate);
	}

	/**
	 * Proceed with the actual installation
	 */
	async function proceedWithInstall(button, methodId, isUpdate) {
		// Disable button immediately to prevent double-clicks
		button.disabled = true;
		button.classList.add("opacity-50", "cursor-not-allowed");
		button.dataset.originalText = button.innerHTML;
		button.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i> Starting...';

		// Check if job manager is available (new system)
		if (window.jobManager && window.startInstallJob) {
			// Use the new job-based system
			try {
				const job = await window.startInstallJob(methodId, isUpdate);
				console.log("[Install] Job started with new system:", job);

				// Update button to show progress
				button.innerHTML =
					'<i class="bx bx-loader-alt bx-spin"></i> ' +
					(isUpdate ? "Updating..." : "Installing...");
				button.dataset.jobId = job.id;
				// Keep disabled but remove the visual indicators
				button.classList.remove("opacity-50", "cursor-not-allowed");

				// Register callback for job completion
				window.jobManager.onJobComplete(job.id, (completedJob) => {
					// Re-enable button after a delay
					setTimeout(() => {
						button.disabled = false;
						button.innerHTML =
							button.dataset.originalText || (isUpdate ? "Update" : "Install");
						button.classList.remove("opacity-50", "cursor-not-allowed");
						delete button.dataset.jobId;
					}, 500);
				});
			} catch (error) {
				console.error("[Install] Error starting job:", error);

				// Handle 409 Conflict specially - job already running
				if (
					error.status === 409 ||
					(error.message && error.message.includes("409"))
				) {
					if (window.showToast) {
						window.showToast(
							"Installation already in progress. Check active tasks for details.",
							"info",
							4000,
						);
					}
					// Keep button disabled for a bit to let user see the message
					setTimeout(() => {
						button.disabled = false;
						button.innerHTML =
							button.dataset.originalText || (isUpdate ? "Update" : "Install");
						button.classList.remove("opacity-50", "cursor-not-allowed");
					}, 1500);
				} else {
					// Other errors - show and re-enable
					if (window.showToast) {
						window.showToast(
							error.message || "Failed to start installation",
							"error",
						);
					}
					button.disabled = false;
					button.innerHTML =
						button.dataset.originalText || (isUpdate ? "Update" : "Install");
					button.classList.remove("opacity-50", "cursor-not-allowed");
				}
			}
			return;
		}

		// Fallback to old session-based system
		console.log("[Install] Using legacy session-based system");

		// Show progress modal
		showInstallProgress(methodId, isUpdate);

		// Disable button during installation
		button.disabled = true;
		button.dataset.originalText = button.innerHTML;
		button.innerHTML =
			'<i class="bx bx-loader-alt bx-spin"></i> ' +
			(isUpdate ? "Updating..." : "Installing...");

		try {
			// Choose the right endpoint
			const endpoint = isUpdate ? "/api/apps/update" : "/api/install";

			// Start installation
			const response = await fetch(endpoint, {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
				},
				body: JSON.stringify({
					method_id: methodId,
					install_uosc: true,
				}),
			});

			if (!response.ok) {
				throw new Error(`HTTP error! status: ${response.status}`);
			}

			const data = await response.json();

			if (!data.session_id) {
				throw new Error("No session_id returned from server");
			}

			activeSessionId = data.session_id;
			console.log(
				"[Install]",
				isUpdate ? "Update" : "Installation",
				"started, session_id:",
				activeSessionId,
			);

			// Connect to SSE stream
			connectToSSEStream(activeSessionId);
		} catch (error) {
			console.error("[Install] Error starting installation:", error);
			showErrorMessage(error.message);
			hideInstallProgress();

			// Re-enable button
			button.disabled = false;
			button.innerHTML = button.dataset.originalText || "Install";
		}
	}

	/**
	 * Handle uninstall button click
	 */
	async function handleUninstallClick(button) {
		// Get uninstall parameters
		const appName = button.dataset.uninstallAppName;
		const appType = button.dataset.uninstallAppType;
		const installMethod = button.dataset.uninstallMethod;

		if (!appName || !appType || !installMethod) {
			console.error("[Install] Missing uninstall parameters");
			showError("Missing uninstall parameters");
			return;
		}

		// Check if this method requires sudo and we need to prompt for password
		if (window.PasswordModal && window.PasswordModal.methodRequiresSudo(installMethod)) {
			// Ensure password is available before proceeding
			const hasPassword = await window.PasswordModal.ensurePassword(installMethod, () => {
				// This callback is executed after password is confirmed
				showUninstallConfirmation(button, appName, appType, installMethod);
			});
			
			// If modal was shown (no password stored), return and wait for callback
			if (!hasPassword) {
				return;
			}
		}

		// Proceed with confirmation (no sudo needed or password already stored)
		showUninstallConfirmation(button, appName, appType, installMethod);
	}

	/**
	 * Show uninstall confirmation modal
	 */
	function showUninstallConfirmation(button, appName, appType, installMethod) {
		// Show confirmation modal using the modal system
		if (window.MPVRocksModal && window.MPVRocksModal.confirm) {
			window.MPVRocksModal.confirm(
				"Uninstall " + appName + "?",
				"Are you sure you want to uninstall " +
					appName +
					"? This will remove the application from your system.",
				"Uninstall",
				"Cancel",
				() => {
					// Proceed with uninstall after confirmation
					performUninstall(button, appName, appType, installMethod);
				},
				true, // isDanger = true (red button)
			);
		} else {
			// Fallback to browser confirm if modal system is not available
			if (confirm("Are you sure you want to uninstall " + appName + "?")) {
				performUninstall(button, appName, appType, installMethod);
			}
		}
	}

	/**
	 * Perform the actual uninstallation (called after confirmation)
	 */
	async function performUninstall(button, appName, appType, installMethod) {
		// Disable button during uninstallation
		button.disabled = true;
		button.dataset.originalText = button.innerHTML;
		button.innerHTML =
			'<i class="bx bx-loader-alt bx-spin"></i> Uninstalling...';

		try {
			// Use job-based API if available (new system)
			const jobApiAvailable = window.jobManager && window.jobManager.activeJobs;

			if (jobApiAvailable) {
				// Use new job-based uninstall endpoint
				const response = await fetch("/api/uninstall/job", {
					method: "POST",
					headers: {
						"Content-Type": "application/json",
					},
					body: JSON.stringify({
						app_name: appName,
						app_type: appType,
						install_method: installMethod,
					}),
				});

				if (!response.ok) {
					throw new Error(`HTTP error! status: ${response.status}`);
				}

				const data = await response.json();
				console.log("[Install] Uninstall job started:", data.job_id);

				// JobManager will handle the rest via SSE
				// Re-enable button after a delay
				setTimeout(() => {
					button.disabled = false;
					button.innerHTML = button.dataset.originalText || "Uninstall";
				}, 2000);
			} else {
				// Fallback to legacy session-based API
				showUninstallProgress(appName);

				const response = await fetch("/api/uninstall", {
					method: "POST",
					headers: {
						"Content-Type": "application/json",
					},
					body: JSON.stringify({
						app_name: appName,
						app_type: appType,
						install_method: installMethod,
					}),
				});

				if (!response.ok) {
					throw new Error(`HTTP error! status: ${response.status}`);
				}

				const data = await response.json();

				if (!data.session_id) {
					throw new Error("No session_id returned from server");
				}

				activeSessionId = data.session_id;
				console.log(
					"[Install] Uninstallation started, session_id:",
					activeSessionId,
				);

				// Connect to SSE stream
				connectToSSEStream(activeSessionId);
			}
		} catch (error) {
			console.error("[Install] Error starting uninstallation:", error);
			showErrorMessage(error.message);
			hideInstallProgress();

			// Re-enable button
			button.disabled = false;
			button.innerHTML = button.dataset.originalText || "Uninstall";
		}
	}

	/**
	 * Connect to SSE stream for installation/uninstallation progress
	 */
	function connectToSSEStream(sessionId) {
		const url = `/api/install/stream?session_id=${encodeURIComponent(sessionId)}`;
		console.log("[Install] Connecting to SSE stream:", url);

		eventSource = new EventSource(url);

		// Output events
		eventSource.addEventListener("output", (e) => {
			const message = e.data;
			console.log("[Install] Output:", message);
			appendOutput(message, "info");
		});

		// Error events
		eventSource.addEventListener("error", (e) => {
			const message = e.data;
			console.error("[Install] Error:", message);
			appendOutput("Error: " + message, "error");
			showErrorMessage(message);
		});

		// Done event
		eventSource.addEventListener("done", () => {
			console.log("[Install] Operation completed");
			appendOutput("Operation completed successfully!", "success");
			showCompleteMessage();

			// Close SSE connection
			eventSource.close();
			eventSource = null;

			// Note: No auto-refresh - user must manually refresh page
		});

		// Connection errors
		eventSource.onerror = (err) => {
			console.error("[Install] SSE connection error:", err);
			appendOutput("Connection lost. Please check to logs.", "error");
			showErrorMessage("Connection lost");

			if (eventSource.readyState === EventSource.CLOSED) {
				// Connection closed
				eventSource = null;
			}
		};
	}

	/**
	 * Show installation progress modal/area
	 */
	function showInstallProgress(
		methodId,
		isUpdate = false,
		isUninstall = false,
	) {
		// Create progress area if it doesn't exist
		let progressArea = document.getElementById("install-progress-area");

		if (!progressArea) {
			progressArea = document.createElement("div");
			progressArea.id = "install-progress-area";
			progressArea.className =
				"fixed inset-0 bg-gray-900 bg-opacity-95 z-50 flex items-center justify-center";
			progressArea.innerHTML = `
                <div class="bg-gray-800 rounded-lg shadow-2xl max-w-4xl w-full mx-4 max-h-[80vh] flex flex-col">
                    <div class="p-6 border-b border-gray-700 flex items-center justify-between">
                        <div class="flex items-center gap-3">
                            <i class="bx bx-loader-alt bx-spin text-2xl text-purple-400"></i>
                            <h3 id="install-title" class="text-xl font-semibold text-white">Installing MPV Player</h3>
                        </div>
                        <button onclick="window.MPVRocksInstall.cancel()" class="text-gray-400 hover:text-white transition">
                            <i class="bx bx-x text-2xl"></i>
                        </button>
                    </div>

                    <div class="flex-1 overflow-auto p-6">
                        <div id="install-output" class="bg-gray-900 rounded-lg p-4 font-mono text-sm min-h-[300px] max-h-[400px] overflow-auto">
                            <div class="text-gray-500">Initializing...</div>
                        </div>
                    </div>

                    <div class="p-6 border-t border-gray-700 flex items-center justify-between gap-4">
                        <div id="install-complete-message" class="flex items-center gap-3 hidden">
                            <i class="bx bx-check-circle text-2xl text-green-400"></i>
                            <div>
                                <div class="text-white font-semibold" id="complete-message-title">Installation Complete!</div>
                                <div class="text-gray-400 text-sm">You can now close this modal</div>
                            </div>
                        </div>
                        <button id="install-complete-btn" onclick="window.location.reload()" class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded transition flex items-center gap-2 hidden ml-auto">
                            <i class="bx bx-x"></i>
                            Close
                        </button>
                    </div>
                </div>
            `;
			document.body.appendChild(progressArea);
		} else {
			progressArea.classList.remove("hidden");
		}

		// Update title
		const title = document.getElementById("install-title");
		if (title) {
			if (isUninstall) {
				title.textContent = "Uninstalling MPV Player";
			} else if (isUpdate) {
				title.textContent = "Updating MPV Player";
			} else {
				title.textContent = "Installing MPV Player";
			}
		}

		// Clear previous output
		const output = document.getElementById("install-output");
		if (output) {
			let action;
			if (isUninstall) {
				action = "uninstallation";
			} else if (isUpdate) {
				action = "update";
			} else {
				action = "installation";
			}
			output.innerHTML =
				'<div class="text-gray-500">Starting ' + action + "...</div>";
		}
	}

	/**
	 * Show uninstallation progress modal/area
	 */
	function showUninstallProgress(appName) {
		showInstallProgress("", false, true);
		const title = document.getElementById("install-title");
		if (title) {
			title.textContent = `Uninstalling ${appName}`;
		}
	}

	/**
	 * Hide installation progress modal
	 */
	function hideInstallProgress() {
		const progressArea = document.getElementById("install-progress-area");
		if (progressArea) {
			progressArea.classList.add("hidden");
		}
	}

	/**
	 * Append output to the progress area
	 */
	function appendOutput(message, type = "info") {
		const output = document.getElementById("install-output");
		if (!output) return;

		const line = document.createElement("div");
		line.className = "mb-1 leading-relaxed";

		const timestamp = new Date().toLocaleTimeString();

		switch (type) {
			case "error":
				line.innerHTML = `<span class="text-red-400">[${timestamp}] ${escapeHtml(message)}</span>`;
				break;
			case "success":
				line.innerHTML = `<span class="text-green-400">[${timestamp}] ${escapeHtml(message)}</span>`;
				break;
			default:
				line.innerHTML = `<span class="text-gray-300">[${timestamp}] ${escapeHtml(message)}</span>`;
		}

		output.appendChild(line);

		// Auto-scroll to bottom
		output.scrollTop = output.scrollHeight;
	}

	/**
	 * Show completion message
	 */
	function showCompleteMessage() {
		// Determine if this was an update by checking button state
		const isUpdate =
			document.querySelector('[data-install][data-is-update="true"]') !== null;

		// Show message in footer
		const messageArea = document.getElementById("install-complete-message");
		const messageTitle = document.getElementById("complete-message-title");

		if (messageArea && messageTitle) {
			messageTitle.textContent = isUpdate
				? "Update Complete!"
				: "Installation Complete!";
			messageArea.classList.remove("hidden");
		}

		// Show the Close button
		const closeBtn = document.getElementById("install-complete-btn");
		if (closeBtn) {
			closeBtn.classList.remove("hidden");
		}
	}

	/**
	 * Show error message in footer
	 */
	function showErrorMessage(message) {
		// Show error message in footer
		const messageArea = document.getElementById("install-complete-message");
		const messageTitle = document.getElementById("complete-message-title");
		const messageIcon = messageArea ? messageArea.querySelector("i") : null;

		if (messageArea && messageTitle && messageIcon) {
			messageTitle.textContent = "Error: " + message;
			messageIcon.classList.remove("bx-check-circle", "text-green-400");
			messageIcon.classList.add("bx-x-circle", "text-red-400");
			messageArea.classList.remove("hidden");

			// Show Close button
			const closeBtn = document.getElementById("install-complete-btn");
			if (closeBtn) {
				closeBtn.classList.remove("hidden");
			}
		} else {
			// Fallback: show alert if modal elements don't exist
			showError(message);
		}
	}

	/**
	 * Show error message
	 */
	function showError(message) {
		alert("Installation Error: " + message);
	}

	/**
	 * Cancel installation
	 */
	function cancelInstall() {
		if (eventSource) {
			eventSource.close();
			eventSource = null;
		}

		activeSessionId = null;

		// Hide footer message area
		const messageArea = document.getElementById("install-complete-message");
		const closeBtn = document.getElementById("install-complete-btn");
		if (messageArea) {
			messageArea.classList.add("hidden");
			// Reset icon to success state
			const messageIcon = messageArea.querySelector("i");
			if (messageIcon) {
				messageIcon.classList.remove("bx-x-circle", "text-red-400");
				messageIcon.classList.add("bx-check-circle", "text-green-400");
			}
		}
		if (closeBtn) {
			closeBtn.classList.add("hidden");
		}

		hideInstallProgress();

		// Re-enable all install buttons
		document.querySelectorAll("[data-install]").forEach((button) => {
			button.disabled = false;
			if (button.dataset.originalText) {
				button.innerHTML = button.dataset.originalText;
			}
		});
	}

	/**
	 * Escape HTML to prevent XSS
	 */
	function escapeHtml(text) {
		const div = document.createElement("div");
		div.textContent = text;
		return div.innerHTML;
	}

	// Public API
	window.MPVRocksInstall = {
		init: initInstallHandlers,
		cancel: cancelInstall,
	};

	// Initialize on DOM ready
	if (document.readyState === "loading") {
		document.addEventListener("DOMContentLoaded", initInstallHandlers);
	} else {
		initInstallHandlers();
	}

	// No need to re-initialize after HTMX swaps - event delegation handles it automatically
})();
