/**
 * Copyright (c) 2023 - present TinyEngine Authors.
 * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
 *
 * Use of this source code is governed by an MIT-style license.
 *
 * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
 * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
 *
 */

import { createApp } from 'vue'
import initSvgs from '@opentiny/tiny-engine-svgs'
import i18n from '@opentiny/tiny-engine-common/js/i18n'
import { initMonitor } from '@opentiny/tiny-engine-common/js/monitor'
import { injectGlobalComponents, setGlobalMonacoEditorTheme, Modal, Notify } from '@opentiny/tiny-engine-common'
import TinyThemeTool from '@opentiny/vue-theme/theme-tool'
import { defaultThemeList } from '@opentiny/tiny-engine-theme-base'
import {
  mergeRegistry,
  getMergeMeta,
  getMetaApi,
  META_SERVICE,
  initServices,
  initHook,
  HOOK_NAME,
  useMessage
} from '@opentiny/tiny-engine-meta-register'
import { utils } from '@opentiny/tiny-engine-utils'
import App from './App.vue'
import defaultRegistry from '../registry.js'
import { registerConfigurators } from './registerConfigurators'

const { guid } = utils

const defaultLifeCycles = {
  beforeAppCreate: ({ registry }) => {
    // 合并用户自定义注册表
    mergeRegistry(defaultRegistry, ...(Array.isArray(registry) ? registry : [registry]))

    const appId = getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id
    const config = getMergeMeta('engine.config')

    if (process.env.NODE_ENV === 'development') {
      console.log('custom registry:', registry) // eslint-disable-line
      console.log('default registry:', defaultRegistry) // eslint-disable-line
    }

    // 初始化所有服务
    initServices()

    initHook(HOOK_NAME.useEnv, import.meta.env)
    initHook(HOOK_NAME.useNotify, Notify, { useDefaultExport: true })
    initHook(HOOK_NAME.useModal, Modal)

    const theme = localStorage.getItem(`tiny-engine-theme-${appId}`) || config.theme || 'light'

    new TinyThemeTool(defaultThemeList[theme], defaultThemeList[theme]?.id)
    document.documentElement?.setAttribute?.('data-theme', theme)

    if (import.meta.env.VITE_ERROR_MONITOR === 'true' && import.meta.env.VITE_ERROR_MONITOR_URL) {
      initMonitor(import.meta.env.VITE_ERROR_MONITOR_URL)
    }

    // 这里暴露到 window 是为了让 canvas 可以读取
    window.TinyGlobalConfig = config || {}
  },
  appCreated: ({ app }) => {
    initSvgs(app)
    window.lowcodeI18n = i18n
    app.use(i18n).use(injectGlobalComponents)

    const appId = getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id
    const theme = localStorage.getItem(`tiny-engine-theme-${appId}`) || getMergeMeta('engine.config').theme
    const editorTheme = theme?.includes('dark') ? 'vs-dark' : 'vs'
    setGlobalMonacoEditorTheme(editorTheme)
  }
}

const subscribeSignalFinish = (createAppSignal, timeout = 30000) => {
  return new Promise((resolve, reject) => {
    let finishCount = new Set()
    const len = new Set(createAppSignal).size

    const signalTopicAndSubscriber = createAppSignal.map((name) => ({
      topic: name,
      subscriber: `createAppSignal_${name}_${guid}`
    }))

    const initTimeout = setTimeout(() => {
      // 取消订阅
      signalTopicAndSubscriber.forEach(({ topic, subscriber }) => {
        useMessage().unsubscribe({
          topic,
          subscriber
        })
      })

      reject(new Error(`Signal initialization timeout after ${Math.floor(timeout / 1000)}s.`))
    }, timeout)

    signalTopicAndSubscriber.forEach(({ topic, subscriber }) => {
      useMessage().subscribe({
        topic,
        subscriber,
        callback: () => {
          finishCount.add(topic)

          // 取消订阅
          useMessage().unsubscribe({
            topic,
            subscriber
          })

          if (finishCount.size === len) {
            clearTimeout(initTimeout)
            resolve()
          }
        }
      })
    })
  })
}

export const init = async ({
  selector = '#app',
  registry = [],
  lifeCycles = {},
  configurators = {},
  createAppSignal = [],
  initTimeout = 30000
} = {}) => {
  const { beforeAppCreate, appCreated, appMounted } = lifeCycles

  registerConfigurators(configurators)

  defaultLifeCycles.beforeAppCreate({ registry })
  beforeAppCreate?.({ registry })

  const token = localStorage.getItem('engineToken')
  if (token) {
    if (Array.isArray(createAppSignal) && createAppSignal.length) {
      try {
        await subscribeSignalFinish(createAppSignal, initTimeout)
      } catch (error) {
        // eslint-disable-next-line no-console
        console.warn('信号等待超时或出错:', error)
      }
    }
  }

  const app = createApp(App)
  defaultLifeCycles.appCreated({ app })
  appCreated?.({ app })

  app.mount(selector)
  appMounted?.({ app })

  if (!token) {
    if (Array.isArray(createAppSignal) && createAppSignal.length) {
      try {
        await subscribeSignalFinish(createAppSignal, initTimeout)
      } catch (error) {
        // eslint-disable-next-line no-console
        console.warn('信号等待超时或出错:', error)
      }
    }
  }
  return app
}