import {
  DeesElement,
  property,
  html,
  customElement,
  type TemplateResult,
  css,
  cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';

import type * as monaco from 'monaco-editor';

declare global {
  interface HTMLElementTagNameMap {
    'dees-editor': DeesEditor;
  }
}

@customElement('dees-editor')
export class DeesEditor extends DeesElement {
  // DEMO
  public static demo = () => html` <dees-editor></dees-editor> `;

  // STATIC
  public static monacoDeferred: ReturnType<typeof domtools.plugins.smartpromise.defer>;

  // INSTANCE
  public editorDeferred = domtools.plugins.smartpromise.defer<monaco.editor.IStandaloneCodeEditor>();
  public language = 'typescript';

  @property({
    type: String
  })
  public content = "function hello() {\n\talert('Hello world!');\n}";

  @property({
    type: Object
  })
  public contentSubject = new domtools.plugins.smartrx.rxjs.Subject<string>();

  @property({
    type: Boolean
  })
  public wordWrap: monaco.editor.IStandaloneEditorConstructionOptions['wordWrap'] = 'off';

  constructor() {
    super();
    domtools.DomTools.setupDomTools();
  }

  public static styles = [
    cssManager.defaultStyles,
    css`
      :host {
      }

      * {
        box-sizing: border-box;
      }

      #container {
        position: absolute;
        height: 100%;
        width: 100%;
      }
    `,
  ];

  public render(): TemplateResult {
    return html`
      <div class="mainbox">
        <div id="container"></div>
      </div>
    `;
  }

  public async firstUpdated(
    _changedProperties: Map<string | number | symbol, unknown>
  ): Promise<void> {
    super.firstUpdated(_changedProperties);
    const container = this.shadowRoot.getElementById('container');

    if (!DeesEditor.monacoDeferred) {
      DeesEditor.monacoDeferred = domtools.plugins.smartpromise.defer();
      const scriptUrl = `https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/loader.js`;
      const script = document.createElement('script');
      script.src = scriptUrl;
      script.onload = () => {
        DeesEditor.monacoDeferred.resolve();
      };
      document.head.appendChild(script);
    }
    await DeesEditor.monacoDeferred.promise;

    (window as any).require.config({
      paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor/min/vs' },
    });
    (window as any).require(['vs/editor/editor.main'], async () => {
      const editor = ((window as any).monaco.editor as typeof monaco.editor).create(container, {
        value: this.content,
        language: this.language,
        theme: 'vs-dark',
        useShadowDOM: true,
        fontSize: 16,
        automaticLayout: true,
        wordWrap: this.wordWrap
      });
      this.editorDeferred.resolve(editor);
    });
    const css = await (
      await fetch('https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/editor/editor.main.css')
    ).text();
    const styleElement = document.createElement('style');
    styleElement.textContent = css;
    this.shadowRoot.append(styleElement);


    // editor is setup let do the rest
    const editor = await this.editorDeferred.promise;
    editor.onDidChangeModelContent(async eventArg => {
      this.contentSubject.next(editor.getValue());
    });
    this.contentSubject.next(editor.getValue());
  }
}