import { CDN_BASE, CDN_VERSIONS } from './versions.js';

// Type imports (no runtime overhead)
import type { Terminal, ITerminalOptions } from 'xterm';
import type { FitAddon } from 'xterm-addon-fit';
import type { HLJSApi } from 'highlight.js';
import type ApexChartsType from 'apexcharts';
import type { Editor, EditorOptions } from '@tiptap/core';
import type { StarterKitOptions } from '@tiptap/starter-kit';
import type { UnderlineOptions } from '@tiptap/extension-underline';
import type { TextAlignOptions } from '@tiptap/extension-text-align';
import type { LinkOptions } from '@tiptap/extension-link';

/**
 * Bundle type for xterm and its addons
 */
export interface IXtermBundle {
  Terminal: typeof Terminal;
}

/**
 * Bundle type for xterm-addon-fit
 */
export interface IXtermFitAddonBundle {
  FitAddon: typeof FitAddon;
}

/**
 * Bundle type for xterm-addon-search
 * SearchAddon is loaded from CDN, so we use a minimal interface
 */
export interface IXtermSearchAddonBundle {
  SearchAddon: new () => IXtermSearchAddon;
}

/**
 * Minimal interface for xterm SearchAddon
 */
export interface IXtermSearchAddon {
  activate(terminal: Terminal): void;
  dispose(): void;
  findNext(term: string, searchOptions?: { regex?: boolean; wholeWord?: boolean; caseSensitive?: boolean; incremental?: boolean }): boolean;
  findPrevious(term: string, searchOptions?: { regex?: boolean; wholeWord?: boolean; caseSensitive?: boolean; incremental?: boolean }): boolean;
}

/**
 * Bundle type for Tiptap editor and extensions
 */
export interface ITiptapBundle {
  Editor: typeof Editor;
  StarterKit: { configure: (options?: Partial<StarterKitOptions>) => any };
  Underline: { configure: (options?: Partial<UnderlineOptions>) => any };
  TextAlign: { configure: (options?: Partial<TextAlignOptions>) => any };
  Link: { configure: (options?: Partial<LinkOptions>) => any };
  Typography: { configure: (options?: any) => any };
}

/**
 * Singleton service for lazy-loading heavy libraries from CDN.
 *
 * This reduces initial bundle size by loading libraries only when needed.
 * Libraries are cached after first load to avoid duplicate fetches.
 *
 * @example
 * ```typescript
 * const libLoader = DeesServiceLibLoader.getInstance();
 * const xterm = await libLoader.loadXterm();
 * const terminal = new xterm.Terminal({ ... });
 * ```
 */
export class DeesServiceLibLoader {
  private static instance: DeesServiceLibLoader;

  // Cached library references
  private xtermLib: IXtermBundle | null = null;
  private xtermFitAddonLib: IXtermFitAddonBundle | null = null;
  private xtermSearchAddonLib: IXtermSearchAddonBundle | null = null;
  private highlightJsLib: HLJSApi | null = null;
  private apexChartsLib: typeof ApexChartsType | null = null;
  private tiptapLib: ITiptapBundle | null = null;

  // Loading promises to prevent duplicate concurrent loads
  private xtermLoadingPromise: Promise<IXtermBundle> | null = null;
  private xtermFitAddonLoadingPromise: Promise<IXtermFitAddonBundle> | null = null;
  private xtermSearchAddonLoadingPromise: Promise<IXtermSearchAddonBundle> | null = null;
  private highlightJsLoadingPromise: Promise<HLJSApi> | null = null;
  private apexChartsLoadingPromise: Promise<typeof ApexChartsType> | null = null;
  private tiptapLoadingPromise: Promise<ITiptapBundle> | null = null;

  private constructor() {}

  /**
   * Get the singleton instance of DeesServiceLibLoader
   */
  public static getInstance(): DeesServiceLibLoader {
    if (!DeesServiceLibLoader.instance) {
      DeesServiceLibLoader.instance = new DeesServiceLibLoader();
    }
    return DeesServiceLibLoader.instance;
  }

  /**
   * Load xterm terminal emulator from CDN
   * @returns Promise resolving to xterm module with Terminal class
   */
  public async loadXterm(): Promise<IXtermBundle> {
    if (this.xtermLib) {
      return this.xtermLib;
    }

    if (this.xtermLoadingPromise) {
      return this.xtermLoadingPromise;
    }

    this.xtermLoadingPromise = (async () => {
      const url = `${CDN_BASE}/xterm@${CDN_VERSIONS.xterm}/+esm`;
      const module = await import(/* @vite-ignore */ url);

      // Also load and inject xterm CSS
      await this.injectXtermStyles();

      this.xtermLib = {
        Terminal: module.Terminal,
      };
      return this.xtermLib;
    })();

    return this.xtermLoadingPromise;
  }

  /**
   * Load xterm-addon-fit from CDN
   * @returns Promise resolving to FitAddon class
   */
  public async loadXtermFitAddon(): Promise<IXtermFitAddonBundle> {
    if (this.xtermFitAddonLib) {
      return this.xtermFitAddonLib;
    }

    if (this.xtermFitAddonLoadingPromise) {
      return this.xtermFitAddonLoadingPromise;
    }

    this.xtermFitAddonLoadingPromise = (async () => {
      const url = `${CDN_BASE}/xterm-addon-fit@${CDN_VERSIONS.xtermAddonFit}/+esm`;
      const module = await import(/* @vite-ignore */ url);

      this.xtermFitAddonLib = {
        FitAddon: module.FitAddon,
      };
      return this.xtermFitAddonLib;
    })();

    return this.xtermFitAddonLoadingPromise;
  }

  /**
   * Load xterm-addon-search from CDN
   * @returns Promise resolving to SearchAddon class
   */
  public async loadXtermSearchAddon(): Promise<IXtermSearchAddonBundle> {
    if (this.xtermSearchAddonLib) {
      return this.xtermSearchAddonLib;
    }

    if (this.xtermSearchAddonLoadingPromise) {
      return this.xtermSearchAddonLoadingPromise;
    }

    this.xtermSearchAddonLoadingPromise = (async () => {
      const url = `${CDN_BASE}/xterm-addon-search@${CDN_VERSIONS.xtermAddonSearch}/+esm`;
      const module = await import(/* @vite-ignore */ url);

      this.xtermSearchAddonLib = {
        SearchAddon: module.SearchAddon,
      };
      return this.xtermSearchAddonLib;
    })();

    return this.xtermSearchAddonLoadingPromise;
  }

  /**
   * Inject xterm CSS styles into the document head
   */
  private async injectXtermStyles(): Promise<void> {
    const styleId = 'xterm-cdn-styles';
    if (document.getElementById(styleId)) {
      return; // Already injected
    }

    const cssUrl = `${CDN_BASE}/xterm@${CDN_VERSIONS.xterm}/css/xterm.css`;
    const response = await fetch(cssUrl);
    const cssText = await response.text();

    const style = document.createElement('style');
    style.id = styleId;
    style.textContent = cssText;
    document.head.appendChild(style);
  }

  /**
   * Load highlight.js syntax highlighter from CDN
   * @returns Promise resolving to highlight.js API
   */
  public async loadHighlightJs(): Promise<HLJSApi> {
    if (this.highlightJsLib) {
      return this.highlightJsLib;
    }

    if (this.highlightJsLoadingPromise) {
      return this.highlightJsLoadingPromise;
    }

    this.highlightJsLoadingPromise = (async () => {
      const url = `${CDN_BASE}/highlight.js@${CDN_VERSIONS.highlightJs}/+esm`;
      const module = await import(/* @vite-ignore */ url);

      this.highlightJsLib = module.default;
      return this.highlightJsLib;
    })();

    return this.highlightJsLoadingPromise;
  }

  /**
   * Load ApexCharts charting library from CDN
   * @returns Promise resolving to ApexCharts constructor
   */
  public async loadApexCharts(): Promise<typeof ApexChartsType> {
    if (this.apexChartsLib) {
      return this.apexChartsLib;
    }

    if (this.apexChartsLoadingPromise) {
      return this.apexChartsLoadingPromise;
    }

    this.apexChartsLoadingPromise = (async () => {
      const url = `${CDN_BASE}/apexcharts@${CDN_VERSIONS.apexcharts}/+esm`;
      const module = await import(/* @vite-ignore */ url);

      this.apexChartsLib = module.default;
      return this.apexChartsLib;
    })();

    return this.apexChartsLoadingPromise;
  }

  /**
   * Load Tiptap rich text editor and extensions from CDN
   * @returns Promise resolving to Tiptap bundle with Editor and extensions
   */
  public async loadTiptap(): Promise<ITiptapBundle> {
    if (this.tiptapLib) {
      return this.tiptapLib;
    }

    if (this.tiptapLoadingPromise) {
      return this.tiptapLoadingPromise;
    }

    this.tiptapLoadingPromise = (async () => {
      const version = CDN_VERSIONS.tiptap;

      // Load all Tiptap modules in parallel
      const [
        coreModule,
        starterKitModule,
        underlineModule,
        textAlignModule,
        linkModule,
        typographyModule,
      ] = await Promise.all([
        import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/core@${version}/+esm`),
        import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/starter-kit@${version}/+esm`),
        import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-underline@${version}/+esm`),
        import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-text-align@${version}/+esm`),
        import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-link@${version}/+esm`),
        import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-typography@${version}/+esm`),
      ]);

      this.tiptapLib = {
        Editor: coreModule.Editor,
        StarterKit: starterKitModule.default || starterKitModule.StarterKit,
        Underline: underlineModule.default || underlineModule.Underline,
        TextAlign: textAlignModule.default || textAlignModule.TextAlign,
        Link: linkModule.default || linkModule.Link,
        Typography: typographyModule.default || typographyModule.Typography,
      };

      return this.tiptapLib;
    })();

    return this.tiptapLoadingPromise;
  }

  /**
   * Preload multiple libraries in parallel
   * Useful for warming the cache before components are rendered
   */
  public async preloadAll(): Promise<void> {
    await Promise.all([
      this.loadXterm(),
      this.loadXtermFitAddon(),
      this.loadXtermSearchAddon(),
      this.loadHighlightJs(),
      this.loadApexCharts(),
      this.loadTiptap(),
    ]);
  }

  /**
   * Check if a specific library is already loaded
   */
  public isLoaded(library: 'xterm' | 'xtermFitAddon' | 'xtermSearchAddon' | 'highlightJs' | 'apexCharts' | 'tiptap'): boolean {
    switch (library) {
      case 'xterm':
        return this.xtermLib !== null;
      case 'xtermFitAddon':
        return this.xtermFitAddonLib !== null;
      case 'xtermSearchAddon':
        return this.xtermSearchAddonLib !== null;
      case 'highlightJs':
        return this.highlightJsLib !== null;
      case 'apexCharts':
        return this.apexChartsLib !== null;
      case 'tiptap':
        return this.tiptapLib !== null;
      default:
        return false;
    }
  }
}
