/**
 * @file
 * FlowDrop Workflow Editor Integration for Drupal
 *
 * @description
 * Provides Drupal behavior for initializing and managing the FlowDrop workflow editor.
 * This script automatically mounts editor instances for elements with the
 * [data-flowdrop-editor] attribute.
 *
 * Requires the shared FlowDrop library (flowdrop_ui_components/flowdrop).
 *
 * @param {Function} once The "once" deduplication library.
 * @param {object} Drupal The Drupal API namespace.
 * @param {object} drupalSettings The Drupal settings object.
 */

((once, Drupal, drupalSettings) => {
  /**
   * Initialize the workflow editor in the given container
   *
   * @param {HTMLElement} editorContainer - The container element for the editor
   */
  function initializeEditor(editorContainer) {
    if (editorContainer.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).
    // Do NOT pass `defaults` — that path deep-merges over the user's
    // persisted preference and writes it back, clobbering their choice.
    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(
        editorContainer,
        'FlowDrop Library Not Available',
        'The FlowDrop library could not be loaded. Please ensure flowdrop_ui_components module is enabled.',
      );
      return;
    }

    if (typeof window.FlowDrop.mountFlowDropApp !== 'function') {
      showError(
        editorContainer,
        'Editor mount function not available',
        'The mountFlowDropApp function is not available in the FlowDrop library.',
      );
      return;
    }

    // Parse workflow data
    const workflow =
      parseJsonData(editorContainer.dataset.workflow) ||
      drupalSettings.flowdrop?.workflow;

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

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

    // Prepare initial workflow data
    const initialWorkflow = workflow
      ? {
          id: workflow.id,
          name: workflow.label || workflow.name || 'Untitled Workflow',
          description: workflow.description || '',
          nodes: workflow.nodes || [],
          edges: workflow.edges || [],
          metadata: {
            ...(workflow.metadata || {}),
            schemaVersion: '1.0.0',
            created: workflow.created,
            changed: workflow.changed,
          },
        }
      : {
          id: `workflow_${Date.now()}`,
          name: 'New Workflow',
          description: 'A new FlowDrop workflow',
          nodes: [],
          edges: [],
          metadata: {
            schemaVersion: '1.0.0',
            created: new Date().toISOString(),
            changed: new Date().toISOString(),
          },
        };

    // Parse UI configuration
    const showNavbar = editorContainer.dataset.showNavbar !== 'false';
    const navbarTitle =
      editorContainer.dataset.navbarTitle || initialWorkflow.name;
    const navbarTasks =
      parseJsonData(editorContainer.dataset.navbarTasks) || [];
    const navbarMenuActions =
      parseJsonData(editorContainer.dataset.navbarActions) || [];
    const showSaveButton = editorContainer.dataset.showSaveButton !== 'false';
    const enableKeyboardShortcuts =
      editorContainer.dataset.enableKeyboardShortcuts !== 'false';
    const uiConfig = parseUiConfig(editorContainer.dataset.uiConfig);

    // Build navbar actions
    const navbarActions = [];

    if (showSaveButton) {
      navbarActions.push({
        label: 'Save',
        href: '#',
        variant: 'primary',
        icon: 'mdi:floppy-disk',
        onclick() {
          handleSave(editorContainer);
        },
      });
    }

    // Local-task-derived links (Playground and any contrib-provided tabs).
    navbarTasks.forEach((task) => {
      if (task && task.label && task.href) {
        navbarActions.push({
          label: task.label,
          href: task.href,
          variant: 'secondary',
          group: 'Tasks',
        });
      }
    });

    // Local-action-derived links (Back and any contrib-provided actions).
    navbarMenuActions.forEach((action) => {
      if (action && action.label && action.href) {
        navbarActions.push({
          label: action.label,
          href: action.href,
          variant: 'outline',
          group: 'Actions',
        });
      }
    });

    // Clear loading state
    const loadingElement = editorContainer.querySelector('.fd-editor__loading');
    if (loadingElement) {
      loadingElement.style.display = 'none';
    }

    // Mount the editor
    mountEditor(editorContainer, {
      workflow: initialWorkflow,
      endpointConfig,
      showNavbar,
      navbarTitle,
      navbarActions,
      enableKeyboardShortcuts,
      uiConfig,
    });
  }

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

  /**
   * Build a FlowDrop AuthProvider from the Drupal-supplied endpoint config.
   *
   * FlowDrop 2.0 no longer reads `endpointConfig.auth`; authentication is a
   * pluggable AuthProvider passed at mount time (defaulting to no auth). The
   * Drupal endpoint config service still ships the CSRF header in
   * `endpointConfig.auth.headers` — translate it into a provider so write
   * requests (PUT/POST/DELETE) carry the `X-CSRF-Token` Drupal requires.
   *
   * The provider only needs `getAuthHeaders()`; `isAuthenticated()` was
   * removed from the AuthProvider interface in a later 2.0 build, and request
   * auth is driven entirely by the headers (plus optional 401/403 hooks).
   *
   * @param {object} endpointConfig - The endpoint configuration.
   * @return {object|undefined} An AuthProvider, or undefined when no headers.
   */
  function buildAuthProvider(endpointConfig) {
    const headers = endpointConfig?.auth?.headers;
    if (!headers || Object.keys(headers).length === 0) {
      return undefined;
    }
    return {
      getAuthHeaders: async () => headers,
    };
  }

  /**
   * Parse the ui_config JSON blob from data-ui-config.
   *
   * @param {string|undefined} raw
   *   The raw data-ui-config attribute value, or undefined when unset.
   * @return {{defaults: object|undefined, exposed: string[]|undefined, drafts: object|undefined}}
   *   The parsed defaults, exposed category names and draft policy; keys are
   *   undefined when absent so callers can spread without overriding library
   *   defaults.
   */
  function parseUiConfig(raw) {
    const parsed = parseJsonData(raw);
    if (!parsed || typeof parsed !== 'object') {
      return { defaults: undefined, exposed: undefined, drafts: undefined };
    }
    const defaults =
      parsed.defaults && typeof parsed.defaults === 'object'
        ? parsed.defaults
        : undefined;
    const exposed = Array.isArray(parsed.exposed) ? parsed.exposed : undefined;
    const drafts =
      parsed.drafts && typeof parsed.drafts === 'object'
        ? parsed.drafts
        : undefined;
    return { defaults, exposed, drafts };
  }

  /**
   * Remove leftover FlowDrop drafts from localStorage.
   *
   * Earlier versions of this integration (and the 'local' backend) persist
   * drafts — full workflow JSON, including node config values — in
   * localStorage, where they outlive logout. When drafts are disabled or
   * session-scoped, sweep those legacy entries so they don't linger.
   */
  function sweepLocalStorageDrafts() {
    try {
      const stale = [];
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key && key.startsWith('flowdrop:draft:')) {
          stale.push(key);
        }
      }
      stale.forEach((key) => localStorage.removeItem(key));
    } catch (error) {
      // Storage may be unavailable (privacy mode, disabled) — nothing to sweep.
    }
  }

  /**
   * Mount the FlowDrop editor component
   *
   * @param {HTMLElement} container - The container element
   * @param {object} options - Mount options
   */
  async function mountEditor(container, options) {
    // Set initialized flag immediately to prevent duplicate initialization
    container.dataset.flowdropInitialized = 'true';
    try {
      // Register all form field types (code/template/markdown editors).
      // These are lazy-loaded — the heavy chunks (CodeMirror, EasyMDE) are
      // fetched on demand when this runs.
      if (typeof window.FlowDrop.initializeAllFieldTypes === 'function') {
        await window.FlowDrop.initializeAllFieldTypes();
      }

      // An explicit empty `exposed` list means "hide the gear icon" — there
      // is nothing to configure. An undefined list means "no Drupal config",
      // and the library default (all categories) applies.
      const exposed = options.uiConfig?.exposed;
      const hideSettings = Array.isArray(exposed) && exposed.length === 0;

      // Draft policy from Drupal config. Defaults deliberately diverge from
      // the library ('local'): drafts contain the full workflow JSON
      // including node config values, and localStorage outlives logout — so
      // unless a site builder opts into 'local', drafts are tab-scoped.
      const drafts = options.uiConfig?.drafts || {};
      const autoSaveDraft = drafts.autoSave !== false;
      const draftStorage = drafts.storage === 'local' ? 'local' : 'session';
      if (draftStorage !== 'local' || !autoSaveDraft) {
        sweepLocalStorageDrafts();
      }

      const app = await window.FlowDrop.mountFlowDropApp(container, {
        workflow: options.workflow,
        endpointConfig: options.endpointConfig,
        authProvider: buildAuthProvider(options.endpointConfig),
        height: '100%',
        width: '100%',
        showNavbar: options.showNavbar,
        navbarTitle: options.navbarTitle,
        navbarActions: options.navbarActions,
        showStatus: false,
        settings: options.uiConfig?.defaults,
        settingsCategories: exposed,
        features: { autoSaveDraft },
        draftStorage,
        ...(hideSettings ? { showSettings: false } : {}),
      });

      container.flowdropApp = app;

      if (options.enableKeyboardShortcuts) {
        setupKeyboardShortcuts(container);
      }

      container.dispatchEvent(
        new CustomEvent('flowdrop:editor:init', {
          detail: {
            editorId: container.id,
            app,
            workflow: options.workflow,
          },
          bubbles: true,
        }),
      );
    } catch (error) {
      // Clear initialization flag so retry is possible
      container.removeAttribute('data-flowdrop-initialized');
      showError(
        container,
        'Failed to load workflow editor',
        `An error occurred while initializing the editor: ${error.message}`,
      );
    }
  }

  /**
   * Set up keyboard shortcuts for the editor
   *
   * @param {HTMLElement} container - The editor container
   */
  function setupKeyboardShortcuts(container) {
    const keydownHandler = (event) => {
      if ((event.ctrlKey || event.metaKey) && event.key === 's') {
        event.preventDefault();
        handleSave(container);
      }
    };

    document.addEventListener('keydown', keydownHandler);
    container.keydownHandler = keydownHandler;
  }

  /**
   * Handle save action
   *
   * @param {HTMLElement} container - The editor container
   */
  function handleSave(container) {
    if (typeof window.flowdropSave === 'function') {
      window
        .flowdropSave()
        .then((result) => {
          container.dispatchEvent(
            new CustomEvent('flowdrop:editor:save', {
              detail: { editorId: container.id, success: true, result },
              bubbles: true,
            }),
          );
        })
        .catch((error) => {
          container.dispatchEvent(
            new CustomEvent('flowdrop:editor:save', {
              detail: {
                editorId: container.id,
                success: false,
                error: error.message,
              },
              bubbles: true,
            }),
          );
        });
    } else if (
      container.flowdropApp &&
      typeof container.flowdropApp.save === 'function'
    ) {
      container.flowdropApp.save().catch(() => {
        // Save handler not yet wired up — surface via the editor's error
        // event would require a container reference, so swallow silently.
      });
    }
  }

  /**
   * Cleanup editor instance
   *
   * @param {HTMLElement} container - The editor container
   */
  function cleanupEditor(container) {
    if (container.keydownHandler) {
      document.removeEventListener('keydown', container.keydownHandler);
      container.keydownHandler = null;
    }

    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
   * @return {object} Default endpoint configuration
   */
  function createDefaultEndpointConfig(baseUrl) {
    return {
      baseUrl,
      endpoints: {
        categories: '/categories',
        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',
        },
        executions: {
          execute: '/workflows/{id}/execute',
          status: '/executions/{id}',
          cancel: '/executions/{id}/cancel',
          logs: '/executions/{id}/logs',
          history: '/executions',
        },
        chat: {
          sendMessage: '/workflows/{id}/chat/messages',
          getHistory: '/workflows/{id}/chat/messages',
          clearHistory: '/workflows/{id}/chat/messages',
        },
      },
      timeout: 30000,
      retry: {
        enabled: true,
        maxAttempts: 3,
        delay: 1000,
        backoff: 'exponential',
      },
    };
  }

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

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

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

    const iconDiv = document.createElement('div');
    iconDiv.className = 'fd-editor__error-icon';
    const svgNS = 'http://www.w3.org/2000/svg';
    const 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');
    const circle = document.createElementNS(svgNS, 'circle');
    circle.setAttribute('cx', '12');
    circle.setAttribute('cy', '12');
    circle.setAttribute('r', '10');
    svg.appendChild(circle);
    const line1 = document.createElementNS(svgNS, 'line');
    line1.setAttribute('x1', '12');
    line1.setAttribute('y1', '8');
    line1.setAttribute('x2', '12');
    line1.setAttribute('y2', '12');
    svg.appendChild(line1);
    const 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);

    const h3 = document.createElement('h3');
    h3.className = 'fd-editor__error-title';
    h3.textContent = Drupal.checkPlain(title);
    errorDiv.appendChild(h3);

    const p = document.createElement('p');
    p.className = 'fd-editor__error-message';
    p.textContent = Drupal.checkPlain(message);
    errorDiv.appendChild(p);

    const hint = document.createElement('p');
    hint.className = 'fd-editor__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-editor--error');
  }

  /**
   * Drupal behavior for mounting FlowDrop workflow editor
   *
   * @type {object}
   */
  Drupal.behaviors.flowdropEditor = {
    /**
     * Attach behavior to editor containers
     *
     * @param {HTMLElement} context - The context element to search within
     */
    attach(context) {
      once('flowdrop-editor', '[data-flowdrop-editor]', context).forEach(
        (editorContainer) => {
          initializeEditor(editorContainer);
        },
      );
    },

    /**
     * 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(context, settings, trigger) {
      if (trigger !== 'unload') {
        return;
      }

      const containers = context.querySelectorAll('[data-flowdrop-editor]');
      containers.forEach((editorContainer) => {
        cleanupEditor(editorContainer);
      });
    },
  };

  /**
   * Public API for FlowDrop Editor
   */
  Drupal.flowdropEditor = {
    getApp(editorIdOrElement) {
      const container =
        typeof editorIdOrElement === 'string'
          ? document.getElementById(editorIdOrElement)
          : editorIdOrElement;
      return container?.flowdropApp || null;
    },

    save(editorIdOrElement) {
      const container =
        typeof editorIdOrElement === 'string'
          ? document.getElementById(editorIdOrElement)
          : editorIdOrElement;
      if (container) {
        handleSave(container);
      }
    },

    destroy(editorIdOrElement) {
      const container =
        typeof editorIdOrElement === 'string'
          ? document.getElementById(editorIdOrElement)
          : editorIdOrElement;
      if (container) {
        cleanupEditor(container);
      }
    },
  };
})(once, Drupal, drupalSettings);
