import { Environment } from 'monaco-editor'

// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Window {
  MonacoEnvironment?: Environment | undefined
}

/**
 * Creates `window.MonacoEnvironment` that returns a web worker that is a ES module
 *
 * @param monacoBase Monaco base folder location, e.g. `../node_modules/monaco-editor/`
 */
export function createEnvironment(monacoBase: string): void {
  let base = monacoBase
  if (!base.endsWith('/')) {
    base += '/'
  }
  window.MonacoEnvironment = {
    getWorker: (_moduleId: string, label: string): Worker => {
      let url
      const prefix = `${base}esm/vs/`
      const langPrefix = `${prefix}language/`
      switch (label) {
        case 'json':
          url = `${langPrefix}json/json.worker.js?worker`
          break
        case 'css':
        case 'scss':
        case 'less':
          url = `${langPrefix}css/css.worker.js?worker`
          break
        case 'html':
        case 'handlebars':
        case 'razor':
          url = `${langPrefix}html/html.worker.js?worker`
          break
        case 'javascript':
        case 'typescript':
          url = `${langPrefix}typescript/ts.worker.js?worker`
          break
        default:
          url = `${prefix}editor/editor.worker.js?worker`
          break
      }
      return new Worker(url, {
        type: 'module',
        name: label,
      })
    },
  }
}

/**
 * @param url The script URL
 * @param isModule Whether the script is ES module
 */
export function loadScript(url: string, isModule = false): Promise<void> {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.src = url
    if (isModule) {
      script.type = 'module'
    }
    // script.defer = false;
    script.async = false
    script.onload = (): void => resolve()
    script.onerror = (): void => reject(new Error(`Unable to load ${url}`))
    const s = document.getElementsByTagName('script')[0]
    s.parentNode?.insertBefore(script, s)
  })
}

/**
 * Loads monaco to the global scope.
 *
 * @param monacoBase Monaco base folder location, e.g. `../node_modules/monaco-editor/`
 * @returns Resolved when all scripts are reported to be loaded
 */
export async function loadMonaco(monacoBase: string): Promise<void> {
  let base = monacoBase
  if (!base.endsWith('/')) {
    base += '/'
  }
  const prefix = `${base}min/vs/`
  // @ts-expect-error This is according to Monaco documentation
  window.require = { paths: { vs: prefix } }
  const scripts = [`${prefix}loader.js`, `${prefix}editor/editor.main.js`]
  // , `${prefix}editor/editor.main.nls.js`
  const ps = scripts.map((url) => loadScript(url))
  await Promise.all(ps)
}

/**
 * Awaits until monaco is fully loaded into the document.
 */
export async function monacoReady(): Promise<void> {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let monacoInterval: any
  return new Promise((resolve) => {
    monacoInterval = setInterval(() => {
      // @ts-expect-error This is according to Monaco documentation
      if (window.monaco) {
        clearInterval(monacoInterval)
        resolve()
      }
    }, 20)
  })
}
