/**
 * 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.
 *
 */

/* metaService: engine.service.translate.useTranslate */
import { reactive, ref } from 'vue'
import { utils } from '@opentiny/tiny-engine-utils'
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
import { constants } from '@opentiny/tiny-engine-utils'
import { generateI18n } from '@opentiny/tiny-engine-common/js/vscodeGenerateFile'
import { PROP_DATA_TYPE } from '@opentiny/tiny-engine-common/js/constants'
import { useResource, useCanvas, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'

const { HOST_TYPE } = constants
const state = reactive<{ langs: Record<string, any> }>({
  langs: {}
})

const currentLanguage = ref('zh_CN')
const i18nResource = reactive<{
  messages: Record<string, any>
  locales: any[]
  [x: string]: any
}>({
  messages: {},
  locales: []
})
const i18nApi = '/app-center/api/i18n/entries'
const globalParams = {
  host: '',
  host_type: ''
}

const getLangs = () => state.langs

const setLangs = (newLangs = {}) => {
  state.langs = newLangs
}

const removeI18n = (key = []) => {
  if (!key.length) {
    return
  }

  const langs = getLangs()
  key.forEach((element) => {
    delete langs[element]
  })

  getMetaApi(META_SERVICE.Http).post(`${i18nApi}/bulk/delete`, {
    ...globalParams,
    key_in: key
  })
}

/**
 *
 * @param {Object} obj 国际化对象
 * @param {Boolean} send  为true时表示需要向后台发起请求进行新增或修改
 * @returns
 */

const ensureI18n = (obj: { [x: string]: any; key: string }, send?: boolean) => {
  const { locales } = i18nResource
  const contents = Object.fromEntries(locales.map(({ lang }) => [lang, obj[lang] || '']))
  const langs = getLangs()
  const key = obj.key || utils.guid()

  if (send) {
    const exist = langs[key]

    if (globalParams.host) {
      getMetaApi(META_SERVICE.Http).post(`${i18nApi}/${exist ? 'update' : 'create'}`, {
        ...globalParams,
        key,
        contents
      })
    }

    locales.forEach((lang) => {
      if (i18nResource[lang]?.[key]) {
        i18nResource[lang][key] = contents[lang]
      }
    })

    // VsCode环境生成本地国际化
    if (isVsCodeEnv) {
      generateI18n({
        key,
        contents
      })
    }
  }

  try {
    const messages: Record<string, any> = {}
    Object.entries(contents).forEach(([locale, message]) => {
      messages[locale] = {
        [key]: message
      }
    })

    useCanvas().canvasApi.value?.setLocales?.(messages, true)
  } catch (e) {
    // 不需要处理,有报错的词条会在画布初始化的时候统一调setLocales这个方法
  }

  langs[key] = { key, ...contents, type: PROP_DATA_TYPE.I18N }

  return langs[key]
}

const getI18nData = () => {
  return getMetaApi(META_SERVICE.Http).get(i18nApi, {
    params: { ...globalParams, _limit: -1 }
  })
}

export interface I18nOptions {
  init?: boolean
  local?: any
  host?: string
  hostType?: string
}

const getI18n = async ({ init, local }: I18nOptions): Promise<typeof appSchemaState.langs> => {
  const { appSchemaState } = useResource()

  if (local) {
    const locales = appSchemaState?.langs?.locales || []
    const messages: Record<string, any> = {}
    const langs = getLangs()

    if (Array.isArray(locales)) {
      locales.forEach(({ lang }) => {
        messages[lang] = {}

        Object.entries(langs).forEach(([key, message]) => {
          messages[lang][key] = message[lang]
        })
      })
    }

    return { locales, messages }
  } else {
    const i18n = init ? appSchemaState.langs : await getI18nData()

    return i18n
  }
}

const initI18n = async ({ host, hostType, init, local }: I18nOptions) => {
  globalParams.host = host || ''
  const hostTypeVar = 'host_type'
  globalParams[hostTypeVar] = hostType || HOST_TYPE.App

  const { locales = [], messages = {} } = await getI18n({ host, hostType, init, local })

  const langs = locales.map((item) => item.lang)

  const firstLangData = messages[langs[0] || 'zh_CN']

  i18nResource.locales = locales
  i18nResource.messages = messages

  Object.keys(firstLangData || {}).forEach((key) => {
    const i18n = { key }

    langs.forEach((lang) => messages[lang] && Object.assign(i18n, { [lang]: messages[lang][key] }))
    ensureI18n(i18n)
  })
}

const initAppI18n = async (appId: string) => {
  if (appId) {
    await initI18n({
      host: appId,
      hostType: HOST_TYPE.App
    })
    useCanvas().canvasApi.value?.setLocales?.(i18nResource.messages)
  }
}

const initBlockI18n = async (blockId: string) => {
  if (blockId) {
    await initI18n({
      host: blockId,
      hostType: HOST_TYPE.Block
    })
    useCanvas().canvasApi.value?.setLocales?.(i18nResource.messages)
  }
}

const initBlockLocalI18n = async (langs = {}) => {
  setLangs(langs)
  await initI18n({
    host: '',
    hostType: HOST_TYPE.Block,
    local: true
  })
  useCanvas().canvasApi.value?.setLocales?.(i18nResource.messages)
}

const format = (str = '', params: Record<string, any> = {}) =>
  str.replace(/\$\{(.+?)\}/g, (_substr, key: string) => params[key] || '')

const translate = (obj: { [x: string]: any }) => {
  const { type, key = utils.guid() } = obj || {}

  if (type === PROP_DATA_TYPE.I18N) {
    const langs = getLangs()
    const i18n = langs[key]
    const langData = i18n || obj

    return format(langData[currentLanguage.value] || langData.key, obj.params)
  }

  return obj
}

const getData = () => i18nResource.messages

const batchCreateI18n = ({ host, hostType }: Pick<I18nOptions, 'host' | 'hostType'>) => {
  if (!host) {
    return
  }

  globalParams.host = host
  globalParams.host_type = hostType || ''

  const { locales } = i18nResource
  const langs = getLangs()

  const entries = Object.entries(langs).map(([key, message]) => ({
    key,
    contents: Object.fromEntries(locales.map(({ lang }) => [lang, message[lang]]))
  }))

  getMetaApi(META_SERVICE.Http).post(`${i18nApi}/batch/create`, {
    ...globalParams,
    entries
  })
}

export default () => {
  return {
    i18nResource,
    currentLanguage,
    getLangs,
    setLangs,
    getData,
    translate,
    removeI18n,
    ensureI18n,
    initI18n,
    batchCreateI18n,
    initAppI18n,
    initBlockI18n,
    getI18nData,
    initBlockLocalI18n
  }
}