import type { Ref } from 'vue'
import { computed } from 'vue'

import { useStyleRegister } from '../../_util/cssinjs'
import type { CSSInterpolation } from '../../_util/cssinjs'
import { useConfigContextInject } from '../../config-provider/context'
import { genCommonStyle, genLinkStyle } from '../../style'
import type { ComponentTokenMap, GlobalToken } from '../interface'
import type { UseComponentStyleResult } from '../internal'
import { mergeToken, statisticToken, useToken } from '../internal'

export type OverrideTokenWithoutDerivative = ComponentTokenMap
export type OverrideComponent = keyof OverrideTokenWithoutDerivative
export type GlobalTokenWithComponent<ComponentName extends OverrideComponent> = GlobalToken &
  ComponentTokenMap[ComponentName]

export interface StyleInfo<ComponentName extends OverrideComponent> {
  hashId: string
  prefixCls: string
  rootPrefixCls: string
  iconPrefixCls: string
  overrideComponentToken: ComponentTokenMap[ComponentName]
}

export type TokenWithCommonCls<T> = T & {
  /** Wrap component class with `.` prefix */
  componentCls: string
  /** Origin prefix which do not have `.` prefix */
  prefixCls: string
  /** Wrap icon class with `.` prefix */
  iconCls: string
  /** Wrap ant prefixCls class with `.` prefix */
  antCls: string
}
export type FullToken<ComponentName extends OverrideComponent> = TokenWithCommonCls<
  GlobalTokenWithComponent<ComponentName>
>

export default function genComponentStyleHook<ComponentName extends OverrideComponent>(
  component: ComponentName,
  styleFn: (token: FullToken<ComponentName>, info: StyleInfo<ComponentName>) => CSSInterpolation,
  getDefaultToken?:
    | OverrideTokenWithoutDerivative[ComponentName]
    | ((token: GlobalToken) => OverrideTokenWithoutDerivative[ComponentName]),
) {
  return (_prefixCls?: Ref<string>): UseComponentStyleResult => {
    const prefixCls = computed(() => _prefixCls?.value)
    const [theme, token, hashId] = useToken()
    const { getPrefixCls, iconPrefixCls } = useConfigContextInject()
    const rootPrefixCls = computed(() => getPrefixCls())
    const sharedInfo = computed(() => {
      return {
        theme: theme.value,
        token: token.value,
        hashId: hashId.value,
        path: ['Shared', rootPrefixCls.value],
      }
    })
    // Generate style for all a tags in antd component.
    useStyleRegister(sharedInfo, () => [
      {
        // Link
        '&': genLinkStyle(token.value),
      },
    ])
    const componentInfo = computed(() => {
      return {
        theme: theme.value,
        token: token.value,
        hashId: hashId.value,
        path: [component, prefixCls.value, iconPrefixCls.value],
      }
    })
    return [
      useStyleRegister(componentInfo, () => {
        const { token: proxyToken, flush } = statisticToken(token.value)

        const defaultComponentToken =
          typeof getDefaultToken === 'function'
            ? (getDefaultToken as any)(proxyToken)
            : getDefaultToken
        const mergedComponentToken = { ...defaultComponentToken, ...token.value[component] }

        const componentCls = `.${prefixCls.value}`
        const mergedToken = mergeToken<
          TokenWithCommonCls<GlobalTokenWithComponent<OverrideComponent>>
        >(
          proxyToken,
          {
            componentCls,
            prefixCls: prefixCls.value,
            iconCls: `.${iconPrefixCls.value}`,
            antCls: `.${rootPrefixCls.value}`,
          },
          mergedComponentToken,
        )
        const styleInterpolation = styleFn(mergedToken as unknown as FullToken<ComponentName>, {
          hashId: hashId.value,
          prefixCls: prefixCls.value,
          rootPrefixCls: rootPrefixCls.value,
          iconPrefixCls: iconPrefixCls.value,
          overrideComponentToken: token.value[component],
        })
        flush(component, mergedComponentToken)
        return [genCommonStyle(token.value, prefixCls.value), styleInterpolation]
      }),
      hashId,
    ]
  }
}
