import { componentToSettings } from '@/core/settings'
import { isBuiltInComponent } from './built-in-components'
import { ComponentMetadata, componentsMap } from './component'
import * as bisector from './bisector/api'
import { BisectorOptions } from './bisector/options'

/**
 * 安装自定义组件
 * @param code 组件代码
 */
export const installComponent = async (code: string) => {
  const { components } = await import('./component')
  const { loadFeatureCode } = await import('@/core/external-input')
  let component: ComponentMetadata
  try {
    component = loadFeatureCode(code) as ComponentMetadata
  } catch (e) {
    throw new Error('无效的组件代码', { cause: e })
  }
  const { settings } = await import('@/core/settings')
  if (isBuiltInComponent(component.name)) {
    throw new Error(`不能覆盖内置组件'${component.name}', 请更换名称`)
  }
  const userMetadata = {
    ...lodash.omit(
      component,
      'entry',
      'widget',
      'instantStyles',
      'reload',
      'unload',
      'plugin',
      'urlInclude',
      'urlExclude',
    ),
  }
  const existingComponent = settings.userComponents[component.name]
  if (existingComponent) {
    existingComponent.code = code
    existingComponent.metadata = userMetadata
    const defaultSettings = componentToSettings(component)
    lodash.defaultsDeep(
      existingComponent.settings.options,
      lodash.pickBy(defaultSettings.options, (value, key) => {
        const isArray = Array.isArray(value)
        if (isArray) {
          return lodash.get(existingComponent.settings.options, key) === undefined
        }
        return true
      }),
    )
    return {
      metadata: component,
      message: `已更新组件'${component.displayName}', 刷新后生效`,
    }
  }
  settings.userComponents[component.name] = {
    code,
    metadata: userMetadata,
    settings: componentToSettings(component),
  }
  components.push(component)
  componentsMap[component.name] = component
  return {
    metadata: component,
    message: `已安装组件'${component.displayName}', 刷新后生效`,
  }
}

/**
 * 卸载自定义组件
 * @param nameOrDisplayName 组件的名称(`name`或`displayName`)
 */
export const uninstallComponent = async (nameOrDisplayName: string) => {
  const { settings } = await import('@/core/settings')
  const { components } = await import('./component')
  const existingComponent = Object.entries(settings.userComponents).find(
    ([
      name,
      {
        metadata: { displayName },
      },
    ]) => {
      if (name === nameOrDisplayName || displayName === nameOrDisplayName) {
        return true
      }
      return false
    },
  )
  if (!existingComponent) {
    throw new Error(`没有找到与名称'${nameOrDisplayName}'相关联的组件`)
  }
  const [name, { metadata, settings: componentSettings }] = existingComponent
  // 如果已加载
  const index = components.findIndex(it => it.name === name)
  if (index !== -1) {
    // 移除可能的 instantStyles
    const { instantStyles } = components[index]
    if (instantStyles) {
      const { removeInstantStyle } = await import('@/core/style')
      instantStyles.forEach(s => removeInstantStyle(s))
    }
    // 移除可能的 widgets
    componentSettings.enabled = false
    components.splice(index, 1)
    delete componentsMap[name]
  }
  delete settings.userComponents[name]
  return {
    metadata,
    message: `已卸载组件'${metadata.displayName}, 刷新后生效'`,
  }
}

/**
 * 切换自定义组件的开关状态
 * @param nameOrDisplayName 组件的名称(`name`或`displayName`)
 */
export const toggleComponent = async (nameOrDisplayName: string) => {
  const { settings } = await import('@/core/settings')
  const existingComponent = Object.entries(settings.userComponents).find(
    ([
      name,
      {
        metadata: { displayName },
      },
    ]) => {
      if (name === nameOrDisplayName || displayName === nameOrDisplayName) {
        return true
      }
      return false
    },
  )
  if (!existingComponent) {
    throw new Error(`没有找到与名称'${nameOrDisplayName}'相关联的组件`)
  }
  const [, userComponent] = existingComponent
  userComponent.settings.enabled = !userComponent.settings.enabled
  const { enabled } = userComponent.settings
  const { displayName } = userComponent.metadata
  return `已${enabled ? '开启' : '关闭'}组件'${displayName}', 可能需要刷新后才能生效`
}

/**
 * 二等分自定义组件的开关状态
 *
 * @param options 二等分选项
 * @returns
 */
export const bisectComponent = async (options?: BisectorOptions) => {
  if (options) {
    bisector.setOptions(options)
  }
  return bisector
}
