/**
 * FlowDrop Shared Library for Drupal
 *
 * This module provides a unified export of FlowDrop functionality for use
 * in Drupal Single Directory Components (SDC). It combines all FlowDrop
 * capabilities - workflow editor, schema forms, playground, and display
 * components - into a single shared library.
 *
 * Heavy optional features (CodeMirror code editors, EasyMDE markdown editor)
 * are lazy-loaded via dynamic import() and only fetched when their
 * registration functions are called.
 *
 * @example
 * // The library exposes window.FlowDrop with all exports
 * const { mountFlowDropApp, mountSchemaForm, mountPlayground } = window.FlowDrop;
 *
 * // Mount workflow editor
 * const editor = await mountFlowDropApp(container, { workflow, endpointConfig });
 *
 * // Mount schema form (lazy-loads the form chunk)
 * const form = await mountSchemaForm(container, { schema, values, onChange });
 *
 * // Mount playground
 * const playground = await mountPlayground(container, { workflowId, endpointConfig });
 */

/**
 * Library version — matches the Drupal module release.
 * Consumers can check `window.FlowDrop.VERSION` to verify API compatibility.
 */
export const VERSION = '1.0.0-alpha4';

/**
 * Import base styles from the FlowDrop package
 */
import '@flowdrop/flowdrop/styles/base.css';
import '@flowdrop/flowdrop/styles/toast.css';

import type { Component } from 'svelte';
import type {
  MountedFlowDropApp,
  FlowDropMountOptions,
} from '@flowdrop/flowdrop/core';
import type {
  Workflow,
  PortConfig,
  CategoryDefinition,
} from '@flowdrop/flowdrop/core';
import type { EndpointConfig } from '@flowdrop/flowdrop/core';
import type {
  MountedPlayground,
  PlaygroundMountOptions,
  PlaygroundStudioMountOptions,
  PlaygroundAppMountOptions,
} from '@flowdrop/flowdrop/playground';

/**
 * Mount options for the WorkflowEditor component (without navbar).
 * A subset of FlowDropMountOptions focused on editor-only configuration.
 */
export interface WorkflowEditorMountOptions {
  /** Initial workflow to load */
  workflow?: Workflow;
  /** API endpoint configuration */
  endpointConfig?: EndpointConfig;
  /** Port configuration for connections */
  portConfig?: PortConfig;
  /** Category definitions for node categories */
  categories?: CategoryDefinition[];
}

// ============================================================================
// Re-export from Sub-modules
// ============================================================================
// We import from specific sub-modules instead of the catch-all main entry
// to avoid pulling in FormFieldFull (which statically imports CodeMirror).
// This enables CodeMirror to be lazy-loaded as a separate chunk.

/** Core types and utilities (no heavy dependencies) */
export * from '@flowdrop/flowdrop/core';

// ============================================================================
// Workflow Editor Exports — Lazy-loaded
// ============================================================================
// The editor module includes @xyflow/svelte and all node components (~350 KB).
// It's lazy-loaded so pages that only need SchemaForm or Playground don't
// pay the cost of editor code.

/** Cached module reference for @flowdrop/flowdrop/editor */
let _editorModule: typeof import('@flowdrop/flowdrop/editor') | null = null;

/** Load the editor module on demand */
async function loadEditorModule() {
  if (!_editorModule) {
    _editorModule = await import('@flowdrop/flowdrop/editor');
  }
  return _editorModule;
}

// ============================================================================
// Field-registry resolution (FlowDrop 2.0)
// ============================================================================
// In 2.0 the field registry is instance-scoped (`fd.fields`) rather than a
// module singleton — the heavy editor-field installers now take the target
// registry explicitly. The Drupal SDC components call the no-arg registration
// helpers below *before* mounting; we resolve the registry of the page-default
// instance, which is the same instance that:
//   - `mountFlowDropApp` / `mountWorkflowEditor` acquire when no `instanceId`
//     is given (first mount on the page = the default instance), and
//   - a standalone `SchemaForm` resolves via its browser `provideInstance()`
//     fallback.
// So registering here, pre-mount, reaches both the editor and standalone forms
// while keeping the public no-arg `window.FlowDrop.register*` surface stable.

/**
 * Resolve the FieldComponentRegistry of the page-default FlowDrop instance.
 * Lazy-loads the editor module (which exports `getDefaultInstance`) — that
 * module is already loaded on editor pages, and is only pulled on form pages
 * when a code/markdown editor field is explicitly enabled.
 */
async function getDefaultFieldRegistry() {
  const mod = await loadEditorModule();
  return mod.getDefaultInstance().fields;
}

/**
 * Synchronously resolve the page-default field registry *iff* the editor
 * module is already loaded. Used by the `is*Registered` checks, which preserve
 * their 1.x semantics of returning `false` before anything has been loaded.
 */
function getDefaultFieldRegistrySync() {
  return _editorModule?.getDefaultInstance().fields ?? null;
}

/**
 * Mount the full FlowDrop app with navbar (lazy-loads editor chunk)
 */
export async function mountFlowDropApp(
  container: HTMLElement,
  options: FlowDropMountOptions,
): Promise<MountedFlowDropApp> {
  const mod = await loadEditorModule();
  return mod.mountFlowDropApp(container, options);
}

/**
 * Mount the workflow editor component only (lazy-loads editor chunk)
 */
export async function mountWorkflowEditor(
  container: HTMLElement,
  options: WorkflowEditorMountOptions,
): Promise<MountedFlowDropApp> {
  const mod = await loadEditorModule();
  return mod.mountWorkflowEditor(container, options);
}

/**
 * Unmount a FlowDrop app instance
 */
export async function unmountFlowDropApp(
  app: MountedFlowDropApp,
): Promise<void> {
  const mod = await loadEditorModule();
  mod.unmountFlowDropApp(app);
}

/**
 * Get the full editor module for advanced use (stores, helpers, registry, etc.)
 * Lazy-loads the editor chunk if not already loaded.
 */
export async function getEditorModule() {
  return loadEditorModule();
}

// ============================================================================
// Display Exports — Lazy-loaded
// ============================================================================
// Display components pull in DOMPurify + marked. No Drupal consumer imports
// them at the wrapper surface (the playground chunk pulls `display` as its own
// lazy dependency), so they are kept out of the always-loaded entry.

/** Cached module reference for @flowdrop/flowdrop/display */
let _displayModule: typeof import('@flowdrop/flowdrop/display') | null = null;

/**
 * Get the display module (markdown rendering, etc.) on demand.
 * Lazy-loads the display chunk (DOMPurify + marked).
 */
export async function getDisplayModule() {
  if (!_displayModule) {
    _displayModule = await import('@flowdrop/flowdrop/display');
  }
  return _displayModule;
}

// ============================================================================
// Form Exports — Lazy-loaded
// ============================================================================
// `formFieldLightPlugin` already redirects FormField.svelte →
// FormFieldLight.svelte at build time, keeping CodeMirror out of this chunk —
// but the SchemaForm component plus the light field components still weigh
// ~150–200 KB. The only form symbol a Drupal consumer calls is
// `mountSchemaForm` (defined below), which now lazy-loads this chunk, so the
// whole family stays out of the entry. Everything else is reachable via
// `getFormModule()`.

/** Cached module reference for @flowdrop/flowdrop/form */
let _formModule: typeof import('@flowdrop/flowdrop/form') | null = null;

/** Load the form module on demand */
async function loadFormModule() {
  if (!_formModule) {
    _formModule = await import('@flowdrop/flowdrop/form');
  }
  return _formModule;
}

/**
 * Get the form module (SchemaForm and field components) on demand.
 * Lazy-loads the form chunk.
 */
export async function getFormModule() {
  return loadFormModule();
}

// ============================================================================
// Settings & Theme Exports — Lazy-loaded
// ============================================================================
// The settings module bundles the ThemeToggle / SettingsPanel / SettingsModal
// Svelte components alongside the (light) settings stores and services. Theme
// functions (setTheme, toggleTheme, cycleTheme, initializeTheme, …) live in
// `@flowdrop/flowdrop/core` (re-exported eagerly above) and are NOT duplicated
// here. The only settings symbol Drupal consumers call is `initializeSettings`,
// so the module is lazy-loaded and everything else is reachable via
// `getSettingsModule()`.

/** Cached module reference for @flowdrop/flowdrop/settings */
let _settingsModule: typeof import('@flowdrop/flowdrop/settings') | null = null;

/** Load the settings module on demand */
async function loadSettingsModule() {
  if (!_settingsModule) {
    _settingsModule = await import('@flowdrop/flowdrop/settings');
  }
  return _settingsModule;
}

/**
 * Get the settings module (stores, services, and the ThemeToggle /
 * SettingsPanel / SettingsModal components) on demand.
 * Lazy-loads the settings chunk.
 */
export async function getSettingsModule() {
  return loadSettingsModule();
}

/**
 * Initialize FlowDrop settings + theme (lazy-loads the settings chunk).
 *
 * Applies the persisted light/dark/auto theme to the document so the
 * design-token `[data-theme="dark"]` rules take effect. The editor and
 * playground Drupal behaviors call this (fire-and-forget) before mounting.
 */
export async function initializeSettings(
  options?: Parameters<
    typeof import('@flowdrop/flowdrop/settings').initializeSettings
  >[0],
): Promise<void> {
  const mod = await loadSettingsModule();
  mod.initializeSettings(options);
}

// ============================================================================
// Form Code Editor Exports (CodeMirror-based editors) — Lazy-loaded
// ============================================================================

/** Cached module reference for @flowdrop/flowdrop/form/code */
let _codeModule: typeof import('@flowdrop/flowdrop/form/code') | null = null;

/** Load the code editor module on demand */
async function loadCodeModule() {
  if (!_codeModule) {
    _codeModule = await import('@flowdrop/flowdrop/form/code');
  }
  return _codeModule;
}

/**
 * Get the FormCodeEditor component (lazy-loaded)
 */
export async function getFormCodeEditor() {
  const mod = await loadCodeModule();
  return mod.FormCodeEditor;
}

/**
 * Get the FormTemplateEditor component (lazy-loaded)
 */
export async function getFormTemplateEditor() {
  const mod = await loadCodeModule();
  return mod.FormTemplateEditor;
}

/**
 * Matcher for code/JSON editor fields.
 * Delegates to the real matcher once the module is loaded;
 * returns false if the module hasn't been loaded yet.
 */
export function codeEditorFieldMatcher(schema: {
  format?: string;
  type?: string;
}): boolean {
  if (!_codeModule) return false;
  type Arg = Parameters<typeof _codeModule.codeEditorFieldMatcher>[0];
  return _codeModule.codeEditorFieldMatcher(schema as Arg);
}

/**
 * Matcher for template editor fields.
 * Delegates to the real matcher once the module is loaded;
 * returns false if the module hasn't been loaded yet.
 */
export function templateEditorFieldMatcher(schema: {
  format?: string;
  type?: string;
}): boolean {
  if (!_codeModule) return false;
  type Arg = Parameters<typeof _codeModule.templateEditorFieldMatcher>[0];
  return _codeModule.templateEditorFieldMatcher(schema as Arg);
}

/**
 * Register the code/JSON editor field component (lazy-loads CodeMirror)
 */
export async function registerCodeEditorField(
  priority?: number,
): Promise<void> {
  const [mod, fields] = await Promise.all([
    loadCodeModule(),
    getDefaultFieldRegistry(),
  ]);
  mod.registerCodeEditorField(fields, priority);
}

/**
 * Register the template editor field component (lazy-loads CodeMirror)
 */
export async function registerTemplateEditorField(
  priority?: number,
): Promise<void> {
  const [mod, fields] = await Promise.all([
    loadCodeModule(),
    getDefaultFieldRegistry(),
  ]);
  mod.registerTemplateEditorField(fields, priority);
}

/**
 * Register all code-related editor fields (lazy-loads CodeMirror)
 */
export async function registerAllCodeEditors(priority?: number): Promise<void> {
  const [mod, fields] = await Promise.all([
    loadCodeModule(),
    getDefaultFieldRegistry(),
  ]);
  mod.registerAllCodeEditors(fields, priority);
}

/**
 * Register code editor field using an already-imported component (lazy-loads module)
 */
export async function registerCodeEditorFieldWithComponent(
  component: Component,
  priority?: number,
): Promise<void> {
  const [mod, fields] = await Promise.all([
    loadCodeModule(),
    getDefaultFieldRegistry(),
  ]);
  mod.registerCodeEditorFieldWithComponent(fields, component, priority);
}

/**
 * Check if code editor field is registered.
 * Returns false if the code module or instance hasn't been loaded yet.
 */
export function isCodeEditorRegistered(): boolean {
  const fields = getDefaultFieldRegistrySync();
  if (!_codeModule || !fields) return false;
  return _codeModule.isCodeEditorRegistered(fields);
}

/**
 * Check if template editor field is registered.
 * Returns false if the code module or instance hasn't been loaded yet.
 */
export function isTemplateEditorRegistered(): boolean {
  const fields = getDefaultFieldRegistrySync();
  if (!_codeModule || !fields) return false;
  return _codeModule.isTemplateEditorRegistered(fields);
}

// ============================================================================
// Form Markdown Editor Exports (EasyMDE-based editor) — Lazy-loaded
// ============================================================================

/** Cached module reference for @flowdrop/flowdrop/form/markdown */
let _markdownModule: typeof import('@flowdrop/flowdrop/form/markdown') | null =
  null;

/** Load the markdown editor module on demand */
async function loadMarkdownModule() {
  if (!_markdownModule) {
    _markdownModule = await import('@flowdrop/flowdrop/form/markdown');
  }
  return _markdownModule;
}

/**
 * Get the FormMarkdownEditor component (lazy-loaded)
 */
export async function getFormMarkdownEditor() {
  const mod = await loadMarkdownModule();
  return mod.FormMarkdownEditor;
}

/**
 * Matcher for markdown editor fields.
 * Delegates to the real matcher once the module is loaded;
 * returns false if the module hasn't been loaded yet.
 */
export function markdownEditorFieldMatcher(schema: {
  format?: string;
}): boolean {
  if (!_markdownModule) return false;
  type Arg = Parameters<typeof _markdownModule.markdownEditorFieldMatcher>[0];
  return _markdownModule.markdownEditorFieldMatcher(schema as Arg);
}

/**
 * Register the markdown editor field component (lazy-loads EasyMDE)
 */
export async function registerMarkdownEditorField(
  priority?: number,
): Promise<void> {
  const [mod, fields] = await Promise.all([
    loadMarkdownModule(),
    getDefaultFieldRegistry(),
  ]);
  mod.registerMarkdownEditorField(fields, priority);
}

/**
 * Register markdown editor field using an already-imported component (lazy-loads module)
 */
export async function registerMarkdownEditorFieldWithComponent(
  component: Component,
  priority?: number,
): Promise<void> {
  const [mod, fields] = await Promise.all([
    loadMarkdownModule(),
    getDefaultFieldRegistry(),
  ]);
  mod.registerMarkdownEditorFieldWithComponent(fields, component, priority);
}

/**
 * Check if markdown editor field is registered.
 * Returns false if the markdown module or instance hasn't been loaded yet.
 */
export function isMarkdownEditorRegistered(): boolean {
  const fields = getDefaultFieldRegistrySync();
  if (!_markdownModule || !fields) return false;
  return _markdownModule.isMarkdownEditorRegistered(fields);
}

// ============================================================================
// Form Full Module Exports (all field types initialization) — Lazy-loaded
// ============================================================================

/**
 * Initialize all form field types including heavy editors (lazy-loads CodeMirror + EasyMDE).
 * Registers against the page-default instance's field registry (FlowDrop 2.0).
 */
export async function initializeAllFieldTypes(): Promise<void> {
  const [mod, fields] = await Promise.all([
    import('@flowdrop/flowdrop/form/full'),
    getDefaultFieldRegistry(),
  ]);
  mod.initializeAllFieldTypes(fields);
}

/**
 * Check if all field types have been initialized on the page-default instance.
 * Loads the module to check the actual state.
 */
export async function areAllFieldTypesInitialized(): Promise<boolean> {
  const [mod, fields] = await Promise.all([
    import('@flowdrop/flowdrop/form/full'),
    getDefaultFieldRegistry(),
  ]);
  return mod.areAllFieldTypesInitialized(fields);
}

// ============================================================================
// Playground Exports — Lazy-loaded
// ============================================================================
// Playground is a separate feature that can be used independently of the
// editor. It's lazy-loaded so pages that only use the editor or schema form
// don't pay the cost of playground code.

/** Cached module reference for @flowdrop/flowdrop/playground */
let _playgroundModule: typeof import('@flowdrop/flowdrop/playground') | null =
  null;

/** Load the playground module on demand */
async function loadPlaygroundModule() {
  if (!_playgroundModule) {
    _playgroundModule = await import('@flowdrop/flowdrop/playground');
  }
  return _playgroundModule;
}

/**
 * Mount a playground instance (lazy-loads playground chunk)
 */
export async function mountPlayground(
  container: HTMLElement,
  options: PlaygroundMountOptions,
): Promise<MountedPlayground> {
  const mod = await loadPlaygroundModule();
  return mod.mountPlayground(container, options);
}

/**
 * Unmount a playground instance (lazy-loads playground chunk if not loaded)
 */
export async function unmountPlayground(app: MountedPlayground): Promise<void> {
  const mod = await loadPlaygroundModule();
  mod.unmountPlayground(app);
}

/**
 * Mount a PlaygroundStudio instance — integrated split-pane with PipelinePanel
 * (lazy-loads playground chunk)
 */
export async function mountPlaygroundStudio(
  container: HTMLElement,
  options: PlaygroundStudioMountOptions,
): Promise<MountedPlayground> {
  const mod = await loadPlaygroundModule();
  return mod.mountPlaygroundStudio(container, options);
}

/**
 * Mount a PlaygroundApp instance — PlaygroundStudio with FlowDrop Navbar
 * (lazy-loads playground chunk)
 */
export async function mountPlaygroundApp(
  container: HTMLElement,
  options: PlaygroundAppMountOptions,
): Promise<MountedPlayground> {
  const mod = await loadPlaygroundModule();
  return mod.mountPlaygroundApp(container, options);
}

export type { PlaygroundAppMountOptions };

// ============================================================================
// Custom Mount Functions for Drupal / Vanilla JS Integration
// ============================================================================

import { mount, unmount } from 'svelte';
import type { SchemaFormProps } from '@flowdrop/flowdrop/form';

/**
 * Type alias for the Svelte component instance
 */
type SchemaFormComponent = ReturnType<typeof mount>;

/**
 * Interface for the mounted SchemaForm instance
 */
export interface MountedSchemaForm {
  /** Unmount the SchemaForm component and clean up resources */
  destroy: () => void;
  /** Reference to the underlying Svelte component instance */
  component: SchemaFormComponent;
}

/**
 * Mount options for the SchemaForm component
 */
export interface MountSchemaFormOptions {
  /** JSON Schema defining the form structure */
  schema: SchemaFormProps['schema'];
  /** Current form values as key-value pairs */
  values?: SchemaFormProps['values'];
  /** Callback fired when form values change */
  onChange?: SchemaFormProps['onChange'];
  /** Whether to display Save and Cancel action buttons */
  showActions?: SchemaFormProps['showActions'];
  /** Callback fired when the Save button is clicked */
  onSave?: SchemaFormProps['onSave'];
  /** Callback fired when the Cancel button is clicked */
  onCancel?: SchemaFormProps['onCancel'];
  /** Whether the form is in a loading state */
  loading?: SchemaFormProps['loading'];
  /** Whether the form is disabled */
  disabled?: SchemaFormProps['disabled'];
  /** Custom CSS class to apply to the form container */
  class?: SchemaFormProps['class'];
}

/**
 * Mounts a SchemaForm component into the specified container element
 *
 * @param container - The DOM element or CSS selector to mount the form into
 * @param options - Configuration options for the SchemaForm
 * @returns A MountedSchemaForm object with methods to control the mounted instance
 *
 * @example
 * ```typescript
 * const form = mountSchemaForm("#form-container", {
 *   schema: { type: "object", properties: { name: { type: "string" } } },
 *   values: { name: "John" },
 *   onChange: (values) => console.log("Form changed:", values),
 *   onSave: (values) => console.log("Saving:", values)
 * });
 *
 * // Later, cleanup
 * form.destroy();
 * ```
 */
export async function mountSchemaForm(
  container: HTMLElement | string,
  options: MountSchemaFormOptions,
): Promise<MountedSchemaForm> {
  const targetElement =
    typeof container === 'string'
      ? document.querySelector<HTMLElement>(container)
      : container;

  if (!targetElement) {
    throw new Error(
      `SchemaForm mount error: Container element not found. ` +
        `Provided: ${typeof container === 'string' ? container : 'HTMLElement'}`,
    );
  }

  if (!options.schema) {
    throw new Error("SchemaForm mount error: 'schema' prop is required");
  }

  const props: SchemaFormProps = {
    schema: options.schema,
  };

  if (options.values !== undefined) props.values = options.values;
  if (options.onChange !== undefined) props.onChange = options.onChange;
  if (options.showActions !== undefined)
    props.showActions = options.showActions;
  if (options.onSave !== undefined) props.onSave = options.onSave;
  if (options.onCancel !== undefined) props.onCancel = options.onCancel;
  if (options.loading !== undefined) props.loading = options.loading;
  if (options.disabled !== undefined) props.disabled = options.disabled;
  if (options.class !== undefined) props.class = options.class;

  const mountOptions = {
    target: targetElement,
    props,
  };

  const { SchemaForm } = await loadFormModule();
  const component = mount(
    SchemaForm,
    mountOptions as Parameters<typeof mount>[1],
  );

  const mountedInstance: MountedSchemaForm = {
    destroy: () => {
      unmount(component);
    },
    component,
  };

  return mountedInstance;
}
