/**
 * 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
 * const form = 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,
  NodeMetadata,
  PortConfig,
  CategoryDefinition,
} from "@flowdrop/flowdrop/core";
import type { EndpointConfig } from "@flowdrop/flowdrop/core";
import type {
  MountedPlayground,
  PlaygroundMountOptions,
} 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;
  /** Available node types */
  nodes?: NodeMetadata[];
  /** 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;
}

/**
 * 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 components (includes marked for markdown rendering) */
export * from "@flowdrop/flowdrop/display";

/**
 * Form components — safe to use `export *` because formFieldLightPlugin
 * redirects FormField.svelte → FormFieldLight.svelte at build time,
 * keeping CodeMirror out of the main chunk.
 * New exports added to @flowdrop/flowdrop/form are picked up automatically.
 */
export * from "@flowdrop/flowdrop/form";

/**
 * Theme and settings — imported via aliased internal paths to bypass the
 * package.json exports restriction. The "flowdrop-internal" alias is
 * configured in vite.config.ts.
 * Using `export *` so new exports are picked up automatically on upgrade.
 */
export { default as ThemeToggle } from "flowdrop-internal/components/ThemeToggle.svelte";
export { default as SettingsPanel } from "flowdrop-internal/components/SettingsPanel.svelte";
export { default as SettingsModal } from "flowdrop-internal/components/SettingsModal.svelte";
export * from "flowdrop-internal/stores/settingsStore.svelte.js";
export * from "flowdrop-internal/services/settingsService.js";

// ============================================================================
// 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 {
  return _codeModule?.codeEditorFieldMatcher(schema) ?? false;
}

/**
 * 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 {
  return _codeModule?.templateEditorFieldMatcher(schema) ?? false;
}

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

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

/**
 * Register all code-related editor fields (lazy-loads CodeMirror)
 */
export async function registerAllCodeEditors(priority?: number): Promise<void> {
  const mod = await loadCodeModule();
  mod.registerAllCodeEditors(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 = await loadCodeModule();
  mod.registerCodeEditorFieldWithComponent(component, priority);
}

/**
 * Check if code editor field is registered.
 * Returns false if the module hasn't been loaded yet.
 */
export function isCodeEditorRegistered(): boolean {
  return _codeModule?.isCodeEditorRegistered() ?? false;
}

/**
 * Check if template editor field is registered.
 * Returns false if the module hasn't been loaded yet.
 */
export function isTemplateEditorRegistered(): boolean {
  return _codeModule?.isTemplateEditorRegistered() ?? false;
}

// ============================================================================
// 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 {
  return _markdownModule?.markdownEditorFieldMatcher(schema) ?? false;
}

/**
 * Register the markdown editor field component (lazy-loads EasyMDE)
 */
export async function registerMarkdownEditorField(
  priority?: number,
): Promise<void> {
  const mod = await loadMarkdownModule();
  mod.registerMarkdownEditorField(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 = await loadMarkdownModule();
  mod.registerMarkdownEditorFieldWithComponent(component, priority);
}

/**
 * Check if markdown editor field is registered.
 * Returns false if the module hasn't been loaded yet.
 */
export function isMarkdownEditorRegistered(): boolean {
  return _markdownModule?.isMarkdownEditorRegistered() ?? false;
}

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

/**
 * Initialize all form field types including heavy editors (lazy-loads CodeMirror + EasyMDE)
 */
export async function initializeAllFieldTypes(): Promise<void> {
  const mod = await import("@flowdrop/flowdrop/form/full");
  mod.initializeAllFieldTypes();
}

/**
 * Check if all field types have been initialized.
 * Loads the module to check the actual state.
 */
export async function areAllFieldTypesInitialized(): Promise<boolean> {
  const mod = await import("@flowdrop/flowdrop/form/full");
  return mod.areAllFieldTypesInitialized();
}

/**
 * Reset initialization state (useful for testing).
 * Loads the module to reset the actual state.
 */
export async function resetFieldTypeInitialization(): Promise<void> {
  const mod = await import("@flowdrop/flowdrop/form/full");
  mod.resetFieldTypeInitialization();
}

// ============================================================================
// 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);
}

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

import { mount, unmount } from "svelte";
import type { SchemaFormProps } from "@flowdrop/flowdrop/form";
import { SchemaForm } 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"];
  /** Label text for the save button */
  saveLabel?: SchemaFormProps["saveLabel"];
  /** Label text for the cancel button */
  cancelLabel?: SchemaFormProps["cancelLabel"];
  /** 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 function mountSchemaForm(
  container: HTMLElement | string,
  options: MountSchemaFormOptions,
): 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.saveLabel !== undefined) props.saveLabel = options.saveLabel;
  if (options.cancelLabel !== undefined)
    props.cancelLabel = options.cancelLabel;
  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 component = mount(
    SchemaForm,
    mountOptions as Parameters<typeof mount>[1],
  );

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

  return mountedInstance;
}
