'use client'

import { createButton } from '@gluestack-ui/button'
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/icon'
import type { VariantProps } from '@gluestack-ui/nativewind-utils'
import { tva } from '@gluestack-ui/nativewind-utils/tva'
import { useStyleContext, withStyleContext } from '@gluestack-ui/nativewind-utils/withStyleContext'
import { cssInterop } from 'nativewind'
import * as PhosphorIcons from 'phosphor-react-native'
import React from 'react'
import { ActivityIndicator, Pressable, Text, View } from 'react-native'

import { variables } from '@/figma-vars'

const SCOPE = 'BUTTON'

const Root = withStyleContext(Pressable, SCOPE)

const UIButton = createButton({
  Root: Root,
  Text,
  Group: View,
  Spinner: ActivityIndicator,
  Icon: UIIcon,
})

cssInterop(PrimitiveIcon, {
  className: {
    target: 'style',
    nativeStyleToProp: {
      height: true,
      width: true,
      fill: true,
      color: 'classNameColor',
      stroke: true,
    },
  },
})

const buttonStyle = tva({
  base: 'group/button flex-row items-center justify-center',
  variants: {
    variant: {
      primary: 'bg-primary-background data-[active=true]:bg-primary-active data-[disabled=true]:bg-disabled',
      secondary: 'bg-secondary-background data-[active=true]:bg-secondary-active data-[disabled=true]:bg-disabled',
      subtle: 'bg-subtle-background data-[active=true]:bg-subtle-active data-[disabled=true]:bg-disabled',
      ghost: 'bg-ghost-background data-[active=true]:bg-ghost-active data-[disabled=true]:bg-disabled',
      outline:
        'border-outline-border bg-outline-background data-[active=true]:bg-outline-active data-[disabled=true]:bg-disabled',
      destructive:
        'bg-destructive-background data-[active=true]:bg-destructive-active data-[disabled=true]:bg-disabled',
    },
    isLoading: {
      false: '',
      true: '',
    },
    iconOnly: {
      false: '',
      true: '',
    },
    size: {
      sm: 'h-[var(--button-height-sm)] gap-[var(--button-spacing-sm)] rounded-[var(--button-radius-sm)] px-[var(--button-padding-horizontal-sm)]',
      md: 'h-[var(--button-height-md)] gap-[var(--button-spacing-md)] rounded-[var(--button-radius-md)] px-[var(--button-padding-horizontal-md)]',
      lg: 'h-[var(--button-height-lg)] gap-[var(--button-spacing-lg)] rounded-[var(--button-radius-lg)] px-[var(--button-padding-horizontal-lg)]',
    },
  },
  compoundVariants: [
    {
      variant: 'primary',
      isLoading: true,
      class: 'bg-primary-loading',
    },
    {
      variant: 'secondary',
      isLoading: true,
      class: 'bg-secondary-loading',
    },
    {
      variant: 'subtle',
      isLoading: true,
      class: 'bg-subtle-loading',
    },
    {
      variant: 'ghost',
      isLoading: true,
      class: 'bg-ghost-loading',
    },
    {
      variant: 'outline',
      isLoading: true,
      class: 'bg-outline-loading',
    },
    {
      variant: 'destructive',
      isLoading: true,
      class: 'bg-destructive-loading',
    },
    {
      iconOnly: true,
      size: 'sm',
      class:
        'size-[var(--icon-only-button-height-sm)] gap-[var(--icon-only-button-spacing-sm)] rounded-[var(--icon-only-button-radius-sm)] px-[var(--icon-only-button-padding-horizontal-sm)]',
    },
    {
      iconOnly: true,
      size: 'md',
      class:
        'size-[var(--icon-only-button-height-md)] gap-[var(--icon-only-button-spacing-md)] rounded-[var(--icon-only-button-radius-md)] px-[var(--icon-only-button-padding-horizontal-md)]',
    },
    {
      iconOnly: true,
      size: 'lg',
      class:
        'size-[var(--icon-only-button-height-md)] gap-[var(--icon-only-button-spacing-lg)] rounded-[var(--icon-only-button-radius-lg)] px-[var(--icon-only-button-padding-horizontal-md)]',
    },
    {
      variant: 'outline',
      size: 'sm',
      iconOnly: false,
      class: 'border-[length:var(--button-border-sm)]',
    },
    {
      variant: 'outline',
      size: 'md',
      iconOnly: false,
      class: 'border-[length:var(--button-border-md)]',
    },
    {
      variant: 'outline',
      size: 'lg',
      iconOnly: false,
      class: 'border-[length:var(--button-border-lg)]',
    },
    {
      variant: 'outline',
      size: 'sm',
      iconOnly: true,
      class: 'border-[length:var(--icon-only-button-border-sm)]',
    },
    {
      variant: 'outline',
      size: 'md',
      iconOnly: true,
      class: 'border-[length:var(--icon-only-button-border-md)]',
    },
    {
      variant: 'outline',
      size: 'lg',
      iconOnly: true,
      class: 'border-[length:var(--icon-only-button-border-lg)]',
    },
  ],
})

const activityIndicatorStyle = tva({
  base: '',
  parentVariants: {
    variant: {
      primary: '',
      secondary: '',
      subtle: '',
      ghost: '',
      outline: '',
      destructive: '',
    },
    size: {
      sm: 'h-0',
      md: '',
      lg: '',
    },
  },
})

const buttonTextStyle = tva({
  base: 'text-typography-0 font-semibold',
  parentVariants: {
    variant: {
      primary: 'text-primary-foreground data-[disabled=true]:text-disabled-foreground',
      secondary: 'text-secondary-foreground data-[disabled=true]:text-disabled-foreground',
      subtle: 'text-subtle-foreground data-[disabled=true]:text-disabled-foreground',
      ghost: 'text-ghost-foreground data-[disabled=true]:text-disabled-foreground',
      outline: 'text-outline-foreground data-[disabled=true]:text-disabled-foreground',
      destructive: 'text-destructive-foreground data-[disabled=true]:text-disabled-foreground',
    },
    size: {
      sm: 'typography-small',
      md: 'typography-medium',
      lg: 'typography-large',
    },
  },
})

const buttonIconStyle = tva({
  base: '',
  parentVariants: {
    variant: {
      primary: 'fill-primary-foreground',
      secondary: 'fill-secondary-foreground',
      subtle: 'fill-subtle-foreground',
      ghost: 'fill-ghost-foreground',
      outline: 'fill-outline-foreground',
      destructive: 'fill-destructive-foreground',
    },
    size: {
      sm: 'size-icon-size-sm',
      md: 'size-icon-size-md',
      lg: 'size-icon-size-lg',
    },
  },
})

type IButtonTextProps = React.ComponentPropsWithoutRef<typeof UIButton.Text> &
  VariantProps<typeof buttonTextStyle> & {
    className?: string
  }

const ButtonText = React.forwardRef<React.ElementRef<typeof UIButton.Text>, IButtonTextProps>(
  ({ className, variant, size, ...props }, ref) => {
    const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE)

    return (
      <UIButton.Text
        ref={ref}
        numberOfLines={1}
        {...props}
        className={buttonTextStyle({
          parentVariants: {
            variant: parentVariant,
            size: parentSize,
          },
          variant,
          size,
          class: className,
        })}
      />
    )
  },
)

const activityIndicatorColors = {
  primary: variables['theme-color']['primary-foreground'],
  secondary: variables['theme-color']['secondary-foreground'],
  subtle: variables['theme-color']['subtle-foreground'],
  ghost: variables['theme-color']['ghost-foreground'],
  outline: variables['theme-color']['outline-foreground'],
  destructive: variables['theme-color']['destructive-foreground'],
}

const ButtonSpinner = React.forwardRef<
  React.ElementRef<typeof UIButton.Spinner>,
  React.ComponentPropsWithoutRef<typeof UIButton.Spinner> &
    VariantProps<typeof activityIndicatorStyle> & {
      className?: string
    }
>(({ className, ...props }, ref) => {
  const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE)

  const color = activityIndicatorColors[parentVariant as keyof typeof activityIndicatorColors]

  return (
    <UIButton.Spinner
      ref={ref}
      {...props}
      className={activityIndicatorStyle({
        parentVariants: {
          size: parentSize,
          variant: parentVariant,
        },
        class: className,
      })}
      color={color}
    />
  )
})

type IButtonIcon = React.ComponentPropsWithoutRef<typeof UIButton.Icon> &
  VariantProps<typeof buttonIconStyle> & {
    className?: string | undefined
    as?: React.ElementType
    height?: number
    width?: number
    weight?: PhosphorIcons.IconWeight
  }

const ButtonIcon = React.forwardRef<React.ElementRef<typeof UIButton.Icon>, IButtonIcon>(
  ({ className, size, ...props }, ref) => {
    const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE)

    return (
      <UIButton.Icon
        {...props}
        className={buttonIconStyle({
          parentVariants: {
            size: parentSize,
            variant: parentVariant,
          },
          size,
          class: className,
        })}
        ref={ref}
      />
    )
  },
)

type PhosphorIconsList = Omit<typeof PhosphorIcons, 'Icon' | 'IconProps' | 'IconContext' | 'IconWeight'>

type IButtonProps = Omit<React.ComponentPropsWithoutRef<typeof UIButton>, 'context'> &
  VariantProps<typeof buttonStyle> & {
    className?: string
    leadingIcon?: keyof PhosphorIconsList | React.ReactElement
    trailingIcon?: keyof PhosphorIconsList | React.ReactElement
    leadingIconWeight?: PhosphorIcons.IconWeight
    trailingIconWeight?: PhosphorIcons.IconWeight
  }

const Button = React.forwardRef<React.ElementRef<typeof UIButton>, IButtonProps>(
  (
    {
      className,
      variant = 'primary',
      size = 'md',
      isLoading = false,
      iconOnly = false,
      leadingIcon,
      trailingIcon,
      ...props
    },
    ref,
  ) => {
    return (
      <UIButton
        ref={ref}
        {...props}
        className={buttonStyle({
          variant,
          size,
          class: className,
          isLoading,
          iconOnly,
        })}
        disabled={isLoading || props.disabled}
        context={{
          variant,
          size,
        }}
      >
        {leadingIcon && typeof leadingIcon === 'string' ? (
          <ButtonIcon as={PhosphorIcons[leadingIcon] as React.ElementType} weight={props.leadingIconWeight} />
        ) : (
          leadingIcon
        )}
        {isLoading && <ButtonSpinner className="h-0" />}
        {props.children && (
          <ButtonText variant={variant} size={size}>
            {props.children as React.ReactNode}
          </ButtonText>
        )}
        {trailingIcon && typeof trailingIcon === 'string' ? (
          <ButtonIcon as={PhosphorIcons[trailingIcon] as React.ElementType} weight={props.trailingIconWeight} />
        ) : (
          trailingIcon
        )}
      </UIButton>
    )
  },
)

Button.displayName = 'Button'
ButtonText.displayName = 'ButtonText'
ButtonSpinner.displayName = 'ButtonSpinner'
ButtonIcon.displayName = 'ButtonIcon'

export { Button, ButtonText, ButtonSpinner, ButtonIcon }
