/**
 * @file
 * FlowDrop Playground Integration for Drupal
 *
 * @description
 * Provides Drupal behavior for initializing and managing the FlowDrop playground.
 * This script automatically mounts playground instances for elements with the
 * [data-flowdrop-playground] attribute.
 *
 * Supports three display modes:
 * - "standalone": Embedded in the page container (default)
 * - "embedded": Embedded with a close button
 * - "modal": Full-screen modal overlay (recommended for immersive experience)
 *
 * Requires the shared FlowDrop library (flowdrop_ui_components/flowdrop).
 */

(function (once, Drupal, drupalSettings) {
	"use strict";

	/**
	 * Valid display modes for the playground
	 *
	 * @type {string[]}
	 */
	var VALID_MODES = ["standalone", "embedded", "modal"];

	/**
	 * Drupal behavior for mounting FlowDrop playground
	 *
	 * @type {Drupal~behavior}
	 */
	Drupal.behaviors.flowdropPlayground = {
		/**
		 * Attach behavior to playground containers
		 *
		 * @param {HTMLElement} context - The context element to search within
		 * @param {object} settings - Drupal settings object
		 */
		attach: function (context, settings) {
			once("flowdrop-playground", "[data-flowdrop-playground]", context).forEach(
				function (playgroundContainer) {
					initializePlayground(playgroundContainer, settings);
				}
			);
		},

		/**
		 * Detach behavior and cleanup mounted components
		 *
		 * @param {HTMLElement} context - The context element to search within
		 * @param {object} settings - Drupal settings object
		 * @param {string} trigger - The trigger for detachment
		 */
		detach: function (context, settings, trigger) {
			if (trigger !== "unload") {
				return;
			}

			var containers = context.querySelectorAll("[data-flowdrop-playground]");
			containers.forEach(function (playgroundContainer) {
				cleanupPlayground(playgroundContainer);
			});
		}
	};

	/**
	 * Initialize the playground in the given container
	 *
	 * @param {HTMLElement} playgroundContainer - The container element for the playground
	 * @param {object} settings - Drupal settings object
	 */
	function initializePlayground(playgroundContainer, settings) {
		if (playgroundContainer.dataset.flowdropInitialized === "true") {
			return;
		}

		// Apply FlowDrop theme (light/dark/auto) to document so design-tokens
		// [data-theme="dark"] rules apply. Uses initializeSettings() which
		// internally calls initializeTheme() (initializeTheme is tree-shaken
		// from the bundle exports but initializeSettings is available).
		if (typeof window.FlowDrop !== "undefined" && typeof window.FlowDrop.initializeSettings === "function") {
			window.FlowDrop.initializeSettings();
		}

		// Check if shared FlowDrop library is available
		if (typeof window.FlowDrop === "undefined") {
			showError(
				playgroundContainer,
				"FlowDrop Library Not Available",
				"The FlowDrop library could not be loaded. Please ensure flowdrop_ui_components module is enabled."
			);
			return;
		}

		if (typeof window.FlowDrop.mountPlayground !== "function") {
			showError(
				playgroundContainer,
				"Playground mount function not available",
				"The mountPlayground function is not available in the FlowDrop library."
			);
			return;
		}

		// Parse workflow data
		var workflow = parseJsonData(playgroundContainer.dataset.workflow) ||
			drupalSettings.flowdrop?.workflow;

		// Parse endpoint configuration
		var endpointConfig = parseJsonData(playgroundContainer.dataset.endpointConfig) ||
			drupalSettings.flowdrop?.endpointConfig;

		if (!endpointConfig) {
			var baseUrl = drupalSettings.flowdrop?.apiBaseUrl || "/api/flowdrop";
			endpointConfig = createDefaultEndpointConfig(baseUrl);
		}

		// Get configuration from data attributes
		var mode = playgroundContainer.dataset.mode || "standalone";
		var backUrl = playgroundContainer.dataset.backUrl || "/admin/flowdrop/workflows";
		var workflowName = playgroundContainer.dataset.workflowName || "Workflow";
		var initialSessionId = playgroundContainer.dataset.initialSessionId ||
			drupalSettings.flowdrop?.initialSessionId || null;

		// Chat input configuration options (new in FlowDrop 0.0.40).
		// showChatInput: Hide text input to show only Run button.
		// showRunButton: Hide Run button for view-only mode.
		// predefinedMessage: Custom message sent when Run is clicked.
		// autoRun: Auto-execute workflow on load.
		var showChatInput = playgroundContainer.dataset.showChatInput !== "false";
		var showRunButton = playgroundContainer.dataset.showRunButton !== "false";
		var predefinedMessage = playgroundContainer.dataset.predefinedMessage || null;
		var autoRun = playgroundContainer.dataset.autoRun === "true";

		// Validate mode
		if (VALID_MODES.indexOf(mode) === -1) {
			console.warn("[FlowDropPlayground] Invalid mode '" + mode + "', falling back to 'standalone'");
			mode = "standalone";
		}

		// Clear loading state for non-modal modes
		if (mode !== "modal") {
			// Loading element may be in the content container
			var contentContainer = playgroundContainer.querySelector(".fd-playground__content");
			var loadingElement = contentContainer
				? contentContainer.querySelector(".fd-playground__loading")
				: playgroundContainer.querySelector(".fd-playground__loading");
			if (loadingElement) {
				loadingElement.style.display = "none";
			}
		}

		// Mount the playground with the specified mode
		mountPlayground(playgroundContainer, {
			workflow: workflow,
			endpointConfig: endpointConfig,
			mode: mode,
			backUrl: backUrl,
			workflowName: workflowName,
			initialSessionId: initialSessionId,
			showChatInput: showChatInput,
			showRunButton: showRunButton,
			predefinedMessage: predefinedMessage,
			autoRun: autoRun
		});
	}

	/**
	 * Parse JSON data from a string, returning null on failure
	 *
	 * @param {string} jsonString - JSON string to parse
	 * @returns {object|null} Parsed object or null
	 */
	function parseJsonData(jsonString) {
		if (!jsonString) return null;
		try {
			return JSON.parse(jsonString);
		} catch (error) {
			console.warn("[FlowDropPlayground] Failed to parse JSON:", error);
			return null;
		}
	}

	/**
	 * Mount the FlowDrop playground component
	 *
	 * @param {HTMLElement} container - The container element
	 * @param {object} options - Mount options including mode, workflow, and endpointConfig
	 */
	async function mountPlayground(container, options) {
		// Set initialized flag immediately to prevent duplicate initialization
		container.dataset.flowdropInitialized = "true";
		try {
			// Extract workflow ID - this is required by mountPlayground
			var workflowId = options.workflow ? options.workflow.id : null;

			if (!workflowId) {
				throw new Error("Workflow ID is required");
			}

			// Prepare workflow data object (optional, for pre-populating data)
			var workflowData = options.workflow
				? {
						id: options.workflow.id,
						name: options.workflow.label || options.workflow.name || "Untitled Workflow",
						description: options.workflow.description || "",
						nodes: options.workflow.nodes || [],
						edges: options.workflow.edges || [],
						metadata: {
							...(options.workflow.metadata || {}),
							version: "1.0.0",
							created: options.workflow.created,
							changed: options.workflow.changed
						}
					}
				: null;

			// Determine the display mode
			var mode = options.mode || "standalone";
			var backUrl = options.backUrl || "/admin/flowdrop/workflows";

			/**
			 * Close handler for modal and embedded modes.
			 * Navigates to the back URL when the playground is closed.
			 */
			var handleClose = function () {
				// Cleanup the playground instance
				cleanupPlayground(container);

				// Navigate to the back URL
				if (backUrl) {
					window.location.href = backUrl;
				}

				// Dispatch close event for custom handling
				container.dispatchEvent(new CustomEvent("flowdrop:playground:close", {
					detail: { containerId: container.id, workflowId: workflowId },
					bubbles: true
				}));
			};

		// Build mount options based on mode
		var mountOptions = {
			workflowId: workflowId,
			workflow: workflowData,
			endpointConfig: options.endpointConfig,
			mode: mode,
			height: "100%",
			width: "100%",
			initialSessionId: options.initialSessionId || null,
			// Chat input configuration (FlowDrop 0.0.40+).
			// These options must be passed inside the config object.
			config: {
				showChatInput: options.showChatInput !== false,
				showRunButton: options.showRunButton !== false,
				predefinedMessage: options.predefinedMessage || null,
				autoRun: options.autoRun === true
			},
			// Session status change callback (FlowDrop 0.0.61+).
			// Used to restart polling after interrupt resolution and
			// dispatch events for other Drupal behaviors.
			onSessionStatusChange: function (status, previousStatus) {
				handleSessionStatusChange(container, status, previousStatus);
			}
		};

			// Add onClose callback for modal and embedded modes
			if (mode === "modal" || mode === "embedded") {
				mountOptions.onClose = handleClose;
			}

			// Find the content container for mounting (supports header layout)
			// If a .fd-playground__content element exists, mount there to preserve the header
			var mountTarget = container.querySelector(".fd-playground__content") || container;

			// Mount the playground using the FlowDrop library
			var app = await window.FlowDrop.mountPlayground(mountTarget, mountOptions);

			container.flowdropApp = app;

			container.dispatchEvent(new CustomEvent("flowdrop:playground:init", {
				detail: {
					containerId: container.id,
					app: app,
					workflowId: workflowId,
					mode: mode
				},
				bubbles: true
			}));
		} catch (error) {
			// Clear initialization flag so retry is possible
			container.removeAttribute("data-flowdrop-initialized");
			showError(
				container,
				"Failed to load playground",
				"An error occurred while initializing the playground: " + error.message
			);
			console.error("[FlowDropPlayground] Initialization error:", error);
		}
	}

	/**
	 * Handle session status changes from the playground.
	 *
	 * Dispatches a custom event for other Drupal behaviors to react to,
	 * and restarts polling when the workflow resumes after interrupt resolution.
	 *
	 * @param {HTMLElement} container - The playground container
	 * @param {string} status - The new session status
	 * @param {string} previousStatus - The previous session status
	 */
	function handleSessionStatusChange(container, status, previousStatus) {
		var app = container.flowdropApp;
		if (!app) return;

		container.dispatchEvent(new CustomEvent("flowdrop:playground:status-change", {
			detail: { status: status, previousStatus: previousStatus },
			bubbles: true
		}));

		// Restart polling when workflow resumes after interrupt resolution.
		// The backend changes session status from awaiting_input → running when
		// an interrupt is resolved, but polling has already stopped. We need to
		// restart it so the UI picks up new messages.
		if (status === "running" && previousStatus === "awaiting_input") {
			app.startPolling();
		}
	}

	/**
	 * Cleanup playground instance
	 *
	 * @param {HTMLElement} container - The playground container
	 */
	function cleanupPlayground(container) {
		if (container.flowdropApp && typeof container.flowdropApp.destroy === "function") {
			container.flowdropApp.destroy();
			container.flowdropApp = null;
		}

		container.removeAttribute("data-flowdrop-initialized");
	}

	/**
	 * Create default endpoint configuration
	 *
	 * @param {string} baseUrl - The base URL for API endpoints
	 * @returns {object} Default endpoint configuration
	 */
	function createDefaultEndpointConfig(baseUrl) {
		return {
			baseUrl: baseUrl,
			endpoints: {
				nodes: {
					list: "/nodes",
					get: "/nodes/{id}",
					byCategory: "/nodes?category={category}",
					metadata: "/nodes/{id}/metadata"
				},
				workflows: {
					list: "/workflows",
					get: "/workflows/{id}",
					create: "/workflows",
					update: "/workflows/{id}",
					delete: "/workflows/{id}",
					validate: "/workflows/validate",
					export: "/workflows/{id}/export",
					import: "/workflows/import"
				},
			playground: {
				sessions: "/workflows/{id}/playground/sessions",
				session: "/playground/sessions/{sessionId}",
				messages: "/playground/sessions/{sessionId}/messages",
				stop: "/playground/sessions/{sessionId}/stop",
				reset: "/playground/sessions/{sessionId}/reset"
			},
				interrupts: {
					list: "/playground/sessions/{sessionId}/interrupts",
					get: "/interrupts/{interruptId}",
					resolve: "/interrupts/{interruptId}",
					cancel: "/interrupts/{interruptId}/cancel"
				}
			},
			timeout: 30000,
			retry: { enabled: true, maxAttempts: 3, delay: 1000, backoff: "exponential" },
			auth: { type: "none" }
		};
	}

	/**
	 * Display an error message in the playground container
	 *
	 * @param {HTMLElement} container - The container element
	 * @param {string} title - Error title
	 * @param {string} message - Error message details
	 */
	function showError(container, title, message) {
		var loadingElement = container.querySelector(".fd-playground__loading");
		if (loadingElement) {
			loadingElement.remove();
		}

		// Clear container safely
		while (container.firstChild) {
			container.removeChild(container.firstChild);
		}

		// Build error UI with safe DOM construction
		var errorDiv = document.createElement("div");
		errorDiv.className = "fd-playground__error";
		errorDiv.setAttribute("role", "alert");
		errorDiv.setAttribute("aria-live", "assertive");

		var iconDiv = document.createElement("div");
		iconDiv.className = "fd-playground__error-icon";
		var svgNS = "http://www.w3.org/2000/svg";
		var svg = document.createElementNS(svgNS, "svg");
		svg.setAttribute("xmlns", svgNS);
		svg.setAttribute("width", "48");
		svg.setAttribute("height", "48");
		svg.setAttribute("viewBox", "0 0 24 24");
		svg.setAttribute("fill", "none");
		svg.setAttribute("stroke", "currentColor");
		svg.setAttribute("stroke-width", "2");
		var circle = document.createElementNS(svgNS, "circle");
		circle.setAttribute("cx", "12");
		circle.setAttribute("cy", "12");
		circle.setAttribute("r", "10");
		svg.appendChild(circle);
		var line1 = document.createElementNS(svgNS, "line");
		line1.setAttribute("x1", "12");
		line1.setAttribute("y1", "8");
		line1.setAttribute("x2", "12");
		line1.setAttribute("y2", "12");
		svg.appendChild(line1);
		var line2 = document.createElementNS(svgNS, "line");
		line2.setAttribute("x1", "12");
		line2.setAttribute("y1", "16");
		line2.setAttribute("x2", "12.01");
		line2.setAttribute("y2", "16");
		svg.appendChild(line2);
		iconDiv.appendChild(svg);
		errorDiv.appendChild(iconDiv);

		var h3 = document.createElement("h3");
		h3.className = "fd-playground__error-title";
		h3.textContent = Drupal.checkPlain(title);
		errorDiv.appendChild(h3);

		var p = document.createElement("p");
		p.className = "fd-playground__error-message";
		p.textContent = Drupal.checkPlain(message);
		errorDiv.appendChild(p);

		var hint = document.createElement("p");
		hint.className = "fd-playground__error-hint";
		hint.textContent = "Please refresh the page or contact support if the problem persists.";
		errorDiv.appendChild(hint);

		container.appendChild(errorDiv);
		container.classList.add("fd-playground--error");
	}

	/**
	 * Public API for FlowDrop Playground
	 */
	Drupal.flowdropPlayground = {
		/**
		 * Get the playground app instance
		 *
		 * @param {string|HTMLElement} idOrElement - Container ID or element
		 * @returns {object|null} The playground app instance or null
		 */
		getApp: function (idOrElement) {
			var container = typeof idOrElement === "string"
				? document.getElementById(idOrElement)
				: idOrElement;
			return container?.flowdropApp || null;
		},

		/**
		 * Destroy a playground instance
		 *
		 * @param {string|HTMLElement} idOrElement - Container ID or element
		 */
		destroy: function (idOrElement) {
			var container = typeof idOrElement === "string"
				? document.getElementById(idOrElement)
				: idOrElement;
			if (container) {
				cleanupPlayground(container);
			}
		},

		/**
		 * Open a playground in modal mode programmatically.
		 * This creates a temporary container and mounts the playground as a full-screen modal.
		 *
		 * @param {object} options - Configuration options
		 * @param {object} options.workflow - Workflow data object with id, nodes, edges, etc.
		 * @param {object} options.endpointConfig - API endpoint configuration
		 * @param {string} options.backUrl - URL to navigate to when modal is closed
		 * @param {Function} options.onClose - Optional callback when modal is closed (called before navigation)
		 * @returns {Promise<object>} The mounted playground app instance
		 */
		openModal: async function (options) {
			if (typeof window.FlowDrop === "undefined" || typeof window.FlowDrop.mountPlayground !== "function") {
				throw new Error("FlowDrop library is not available");
			}

			if (!options.workflow || !options.workflow.id) {
				throw new Error("Workflow with id is required");
			}

			// Create a temporary container element for tracking
			var containerId = "flowdrop-playground-modal-" + options.workflow.id;
			var existingContainer = document.getElementById(containerId);
			if (existingContainer) {
				// Close existing modal first
				cleanupPlayground(existingContainer);
				existingContainer.remove();
			}

			var container = document.createElement("div");
			container.id = containerId;
			container.setAttribute("data-flowdrop-playground", "");
			container.setAttribute("data-mode", "modal");
			container.style.display = "none"; // Hidden since modal mounts to body
			document.body.appendChild(container);

			// Prepare workflow data
			var workflowData = {
				id: options.workflow.id,
				name: options.workflow.label || options.workflow.name || "Untitled Workflow",
				description: options.workflow.description || "",
				nodes: options.workflow.nodes || [],
				edges: options.workflow.edges || [],
				metadata: options.workflow.metadata || {}
			};

			// Handle close action
			var handleClose = function () {
				// Call custom onClose callback if provided
				if (typeof options.onClose === "function") {
					options.onClose();
				}

				// Cleanup
				cleanupPlayground(container);
				container.remove();

				// Navigate if backUrl is provided
				if (options.backUrl) {
					window.location.href = options.backUrl;
				}
			};

			// Mount in modal mode
			var app = await window.FlowDrop.mountPlayground(container, {
				workflowId: options.workflow.id,
				workflow: workflowData,
				endpointConfig: options.endpointConfig,
				mode: "modal",
				onClose: handleClose
			});

			container.dataset.flowdropInitialized = "true";
			container.flowdropApp = app;

			return app;
		}
	};
})(once, Drupal, drupalSettings);
