<script lang="ts">
  import type monaco from 'monaco-editor';
  import { onDestroy, onMount } from 'svelte';
  import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
  // import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
  // import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
  // import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
  import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
  // @ts-ignore
  import wrqTypes from '@types/webextension-polyfill/namespaces/webRequest.d.ts?raw';
  // @ts-ignore
  import evTypes from '@types/webextension-polyfill/namespaces/events.d.ts?raw';
  import userland from './userland.d.ts?raw';

  let divEl: HTMLDivElement | null = $state(null);
  let editor: monaco.editor.IStandaloneCodeEditor = $state(null as any);
  let Monaco: typeof monaco;
  let {
    defaultValue,
    typeDefs = `import type { UserlandBrowser } from './userland';
declare global {
  /**
   * The subset of the host extension's browser type available to the extension
   * Note: We don't properly sandbox anything. You can likely easily get access to shit outside of here from the browser global.
   */
  declare const browser: UserlandBrowser;
}
`,
    value = $bindable(''),
  }: {
    value?: string;
    defaultValue: string;
    typeDefs?: string;
  } = $props();
  let writeDebounce = false;

  onMount(async () => {
    // @ts-ignore
    globalThis.MonacoEnvironment = {
      getWorker: async (_moduleId: any, label: string) => {
        if (label === 'json') {
          return new (
            await import(
              'monaco-editor/esm/vs/language/json/json.worker?worker'
            )
          ).default();
        }
        // if (label === 'css' || label === 'scss' || label === 'less') {
        //   return new cssWorker();
        // }
        // if (label === 'html' || label === 'handlebars' || label === 'razor') {
        //   return new htmlWorker();
        // }
        if (label === 'typescript' || label === 'javascript') {
          return new tsWorker();
        }
        return new editorWorker();
      },
    };

    Monaco = await import('monaco-editor');
    Monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
      allowNonTsExtensions: true,
      moduleResolution: Monaco.languages.typescript.ModuleResolutionKind.NodeJs,
      module: Monaco.languages.typescript.ModuleKind.ESNext,
      noEmit: true,
      typeRoots: ['node_modules/@types'],
    });
    Monaco.editor.defineTheme('redirext', {
      base: 'vs-dark',
      inherit: true,
      rules: [],
      colors: {
        'editor.foreground': '#dedede',
        'editor.background': '#23222b',
        'editor.selectionBackground': '#4c2889',
        'editor.inactiveSelectionBackground': '#444d56',
        'editor.lineHighlightBackground': '#444d56',
        'editorCursor.foreground': '#ffffff',
        'editorWhitespace.foreground': '#6a737d',
        'editorIndentGuide.background': '#6a737d',
        'editorIndentGuide.activeBackground': '#f6f8fa',
        'editor.selectionHighlightBorder': '#444d56',
      },
    });
    if (!divEl) while (!divEl) await new Promise((rs) => setTimeout(rs, 100));
    for (const [_filename, contents] of Object.entries(
      (await import('./filemap.js')).default
    )) {
      Monaco.languages.typescript.typescriptDefaults.addExtraLib(
        contents
        // ,_filename
      );
    }
    Monaco.languages.typescript.typescriptDefaults.addExtraLib(
      evTypes,
      'node_modules/@types/webextension-polyfill/namespaces/events.d.ts'
    );
    Monaco.languages.typescript.typescriptDefaults.addExtraLib(
      wrqTypes,
      'node_modules/@types/webextension-polyfill/namespaces/webRequest.d.ts'
    );
    Monaco.languages.typescript.typescriptDefaults.addExtraLib(
      `export * from './namespaces/webRequest';`,
      'node_modules/@types/webextension-polyfill/index.d.ts'
    );
    Monaco.languages.typescript.typescriptDefaults.addExtraLib(
      userland,
      'node_modules/@types/redirext/userland.d.ts'
    );
    Monaco.languages.typescript.typescriptDefaults.addExtraLib(
      typeDefs,
      'node_modules/@types/redirext/index.d.ts'
    );
    editor = Monaco.editor.create(divEl, {
      value: defaultValue,
      language: 'typescript',
      theme: 'redirext',
      autoDetectHighContrast: false,
    });
    editor.addCommand(Monaco.KeyMod.Alt | Monaco.KeyCode.Backslash, () => {
      const win = window.open(location.href);
      if (win) {
        const el = document.createElement('div');
        el.setAttribute(
          'style',
          'display:flex;width:100vw;height:100vh;position:fixed;left:0;top:0;align-items:center;justify-content:center;text-align:center;z-index:9999;background:#23222B;'
        );
        el.textContent = 'Popped out';
        document.body.appendChild(el);
        win.addEventListener('close', () => {
          el.textContent = 'Popping In...';
          location.reload();
        });
      }
    });
    editor.getModel()?.onDidChangeContent((e) => {
      writeDebounce = true;
      const upd = () => {
        try {
          value =
            editor.getValue({ lineEnding: '\n', preserveBOM: false }).trim() +
            '\n';
          // console.debug('Updated value from editor to:', value);
        } catch (error) {
          writeDebounce = false;
          throw error;
        }
        setTimeout(() => {
          writeDebounce = false;
        }, 0);
      };
      setTimeout(upd, 0);
    });
  });
  $effect(() => {
    if (editor && !writeDebounce) {
      // console.debug('Updating editor with value', value);
      editor.setValue(value.trim() + '\n');
    }
  });
  onDestroy(() => {
    if (editor) editor.dispose();
  });
</script>

<div
  bind:this={divEl}
  class="h-screen overflow-hidden bg-card box-border max-h-full"
></div>