import { getPx, IProps, th as xth, ThemeGetter, themeGetter } from '@xstyled/styled-components'
import { useMemo } from 'react'
import { useTheme as useStyledTheme } from 'styled-components'

import defaultTheme, { ThemingProps, VuiTheme, VuiThemeExtensions } from '../theme'
import { AnyNumber, AnyString, Dict, merge, runIfFn } from '../utils'

type VuiThemeGetter<ThemeKey extends keyof VuiTheme> = ThemeGetter<keyof VuiTheme[ThemeKey] | AnyString | AnyNumber>

/** Type of the 'th' theme getter function. */
export type ThemeGet = {
  (path: string, defaultValue?: any): (props: IProps) => any
  animation: VuiThemeGetter<'animations'>
  borders: VuiThemeGetter<'borders'>
  color: VuiThemeGetter<'colors'>
  duration: VuiThemeGetter<'durations'>
  font: VuiThemeGetter<'fonts'>
  fontSize: VuiThemeGetter<'fontSizes'>
  fontWeight: VuiThemeGetter<'fontWeights'>
  radius: VuiThemeGetter<'radii'>
  ringWidth: VuiThemeGetter<'ringWidths'>
  screen: VuiThemeGetter<'screens'>
  shadow: VuiThemeGetter<'shadows'>
  size: VuiThemeGetter<'sizes'>
  space: VuiThemeGetter<'spaces'>
  transform: VuiThemeGetter<'transforms'>
  transition: VuiThemeGetter<'transitions'>
  zIndex: VuiThemeGetter<'zIndices'>
}

/** Merges provided theme extensions and overrides with default VUI theme. */
export function extendTheme(extensions: VuiThemeExtensions): VuiTheme {
  return merge(defaultTheme, extensions)
}

/** Removes specific theming keys from the given props. */
export function omitThemingProps<T extends ThemingProps>(props: T) {
  const { size, variant, ...rest } = props // eslint-disable-line @typescript-eslint/no-unused-vars

  return rest
}

/** Re-export of xstyled 'th' with custom typing and screen getter. */
const th = xth as unknown as ThemeGet
th.screen = themeGetter({
  name: 'screen',
  key: 'screens',
  compose: getPx,
})
export { th }

/**
 * Combines baseStyle, size and variant styling from a component's theme configuration.
 * @param component - name of any component configured in the theme
 * @param props - props object of the given component
 */
export function useStyleConfig(component: keyof VuiTheme['components'], props: any = {}) {
  const theme = useTheme()
  const { baseStyle, defaultProps, parts = [], sizes, variants } = (theme?.components[component] as Dict) ?? {}
  const mergedProps = {
    ...props,
    colorScheme: props?.colorScheme ?? defaultProps?.colorScheme,
    size: props?.size ?? defaultProps?.size,
    variant: props?.variant ?? defaultProps?.variant,
  }
  const { colorScheme, size, variant } = mergedProps

  return useMemo(() => {
    const baseStyles = runIfFn(baseStyle, mergedProps) ?? {}
    const sizeStyles = runIfFn(sizes?.[size], mergedProps) ?? {}
    const variantStyles = runIfFn(variants?.[variant], mergedProps) ?? {}

    let styles: Dict = {}

    if (parts.length > 0)
      parts.forEach((part: string) => {
        styles[part] = merge(baseStyles[part] ?? {}, sizeStyles[part] ?? {}, variantStyles[part] ?? {})
      })
    else styles = merge(baseStyles, sizeStyles, variantStyles)

    return styles
  }, [colorScheme, size, variant])
}

/** Returns theme value at the given path. */
export function useTh(path: keyof VuiTheme | AnyString, defaultValue?: any) {
  const theme = useTheme()
  return th(path, defaultValue)({ theme })
}

/** Returns the theme object with extensions. */
export function useTheme(): VuiTheme {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  return useStyledTheme()
}
