import * as React from 'react';
import {createRoot, Root} from 'react-dom/client';
import {configure} from 'mobx';
import {Language} from 'headless-cms-client-shared/Language';
import {ThemeAndLegalConfig} from 'headless-cms-client-shared/lib/Data/Module';
// Imported from the module rather than the ./lib/Translation barrel: the barrel also pulls in
// Translator (and with it the whole HCMS service graph), and this file is already part of a
// type-only import cycle with lib/App/Context.ts.
import {LanguageInput, resolveLocale} from './lib/Translation/resolveLocale';
import {App} from './App';

// MobX 5 (this app's original version) had no action enforcement by default. MobX 6 changed the
// default to `enforceActions: 'observed'`, which - since this codebase mutates @observable fields
// directly from plain event handlers throughout, rather than wrapping every one in @action - now
// logs a console warning on nearly every interaction. That's new noise from the dependency bump,
// not a signal of an actual bug introduced by it, so it's set back to 'never' explicitly here
// (once, at module load, before any store exists) to match the app's original/intended behavior
// instead of silently inheriting whatever MobX's own default happens to be release to release.
configure({enforceActions: 'never'});

export interface ConfigData {
  apiUrl: string;
  middlewareUrl?: string;
  /**
   * UI language. Accepts a bare code ('de') or any BCP-47 tag ('de-DE', 'DE') - the region
   * and script subtags are ignored. Omitted, empty or unsupported falls back to the browser's
   * preferred languages and then to English; see lib/Translation/resolveLocale.ts. Read once,
   * at create() time - the picker has no runtime language switch.
   */
  language?: LanguageInput;
  success: (files: any[]) => void;
  cancel: () => void;
  /**
   * Theme preset: one of the stylesheets built from src/theme/entries/ ('censhare' - the default,
   * compiled into the bundle - or e.g. 'marmind'). Case-insensitive; an unknown name logs a
   * warning and falls back to 'censhare'. A non-default theme's stylesheet is fetched from
   * `themes/<name>.css` next to app(.min).js.
   */
  themeName?: string;
  /** Overrides where the theme stylesheet is fetched from, for hosts that serve it elsewhere. */
  themeUrl?: string;
  /** Per-value overrides of the theme's colors, font and logo, deep-merged onto the preset. */
  theme?: ThemeAndLegalConfig;
}

/** What `appConfig` hands to <App>: every optional field defaulted, the locale normalized. */
export interface ResolvedConfigData extends Omit<ConfigData, 'language' | 'middlewareUrl'> {
  middlewareUrl: string;
  language: Language;
}

export interface ICsMediaLibrary {
  create: (config: ConfigData) => void;
  destroy: () => void;
  resolveLocale: (preferred?: LanguageInput | LanguageInput[]) => Language;
}

class CsMediaLibrary implements ICsMediaLibrary {
  private handler?: HTMLDivElement;
  private root?: Root;
  private config!: ConfigData;

  create = (config: ConfigData): void => {
    // Only ever one picker at a time. Without this, a second create() (e.g. an embedding page
    // reopening it with a different `language`) overwrote `handler`/`root` while the previous
    // React tree stayed mounted: two full-screen overlays (`position: fixed`, z-index 9999999)
    // stacked on top of each other, and closing the visible one only unmounted *that* root -
    // the buried one kept covering the page with a Cancel button that was now a no-op, so the
    // only way out was a full page reload.
    this.destroy();

    this.config = config;

    this.handler = document.createElement('div');
    document.body.appendChild(this.handler);

    this.root = createRoot(this.handler);
    this.root.render(<App config={this.appConfig} />);
  };

  destroy = () => {
    if (this.root) {
      this.root.unmount();
      this.root = undefined;
    }

    // unmount() only empties the container; the div itself stayed in <body> forever, so every
    // open/close cycle left one more orphan behind.
    if (this.handler) {
      this.handler.remove();
      this.handler = undefined;
    }
  };

  // Exposed on the UMD global so an embedding page can show or log which language the picker
  // will actually use without reimplementing the negotiation. Pure - it does not touch the
  // picker. Must stay an arrow-function class field: src/index.ts builds the mock-mode wrapper
  // with `{...CsMediaLibraryImpl}`, which copies own properties only, so a prototype method
  // would silently disappear under `yarn start:mock`.
  resolveLocale = (preferred: LanguageInput | LanguageInput[] = []): Language => resolveLocale(preferred);

  private handleSuccess = (files: any[]): void => {
    if (this.config && this.config.success) {
      this.config.success(files);
    }
    this.onCloseMediaManager();
  };

  private handleCancel = (): void => {
    if (this.config && this.config.cancel) {
      this.config.cancel();
    }
    this.onCloseMediaManager();
  };

  private onCloseMediaManager = (): void => {
    this.destroy();
  };

  private get appConfig(): ResolvedConfigData {
    if (!this.config) {
      throw new Error('Missing config!');
    }

    return {
      apiUrl: (this.config && this.config.apiUrl) || '',
      middlewareUrl: (this.config && this.config.middlewareUrl) || '',
      // The single point where the locale is normalized. Everything downstream - the
      // dictionaries keyed by the Language enum in shared/lib/Translation/Translation.ts, the
      // per-locale HCMS schema-label requests in lib/Translation/Translator.ts, the DayJsLocale
      // lookup - matches on the bare enum value, so a region-tagged code slipping past here
      // does not fall back cleanly to English: it drops the picker's own dictionary entirely
      // and renders every picker phrase as `_Some.Key`.
      language: resolveLocale([this.config.language]),
      success: this.handleSuccess,
      cancel: this.handleCancel,
      themeName: this.config.themeName,
      themeUrl: this.config.themeUrl,
      theme: (this.config && this.config.theme) || undefined,
    };
  }
}

export default new CsMediaLibrary();
