import { makeComponentProps } from '@/composables/component'
import { makeSizeProps, Sizes } from '@/composables/size'
import { InputRulesType } from '@/types/inputRules'
import InputVariant from '@/types/inputVariants'
import { computed, ref } from 'vue'
import { useEventListener } from '@vueuse/core'

import { UIcon } from '@/components/UIcon/UIcon'
import { UTooltip } from '@/components/UTooltip/UTooltip'

import { genericComponent, propsFactory, useRender } from '@/utils'

export const makeUInputProps = propsFactory(
  {
    modelValue: {
      type: String,
      default: '',
      required: true,
    },
    type: {
      type: String,
      default: 'text',
    },
    label: String,
    placeholder: String,
    hint: String,
    tooltipTitle: String,
    tooltipText: String,
    tooltipTheme: {
      type: String,
      default: 'light',
      required: false,
    },
    tooltipPosition: String,
    appendIcon: String,
    prependIcon: String,
    rules: {
      type: Array as () => InputRulesType,
      default: undefined,
      required: false,
    },
    validateOn: {
      type: String,
      default: 'submit',
      required: false,
    },
    variant: {
      type: String,
      default: InputVariant.default,
      required: false,
    },
    disabled: {
      type: Boolean,
      default: false,
      required: false,
    },
    autocomplete: Boolean,
    name: String,
    error: Boolean,
    errorMessages: Array as () => string[],

    ...makeComponentProps(),
    ...makeSizeProps({ size: Sizes.sm }),
  },
  'UInput'
)

export type UInputSlots = {
  default: never
}

export const UInput = genericComponent<UInputSlots>()({
  name: 'UInput',

  props: makeUInputProps(),

  emits: {
    'update:modelValue': (value: string) => true,
    validationSuccess: () => true,
    validationError: () => true,
    blur: () => true,
    input: () => true,
    change: () => true,
    submit: () => true,
    click: (e: MouseEvent) => true,
    'click:append': () => true,
    'click:prepend': () => true,
    'keydown:enter': () => true,
  },

  setup(props, { emit, slots }) {
    const prependIcon = computed(() => {
      if (props.prependIcon) {
        return props.prependIcon
      }
      return ''
    })

    const appendIcon = computed(() => {
      if (props.appendIcon) {
        return props.appendIcon
      }
      return ''
    })

    const hasLabel = !!props.label
    const hasPrepend = !!props.prependIcon
    const hasAppend = !!props.appendIcon
    const isActive = ref(false)
    const isHover = ref(false)
    const inputValue = ref('')

    const handleKeyDown = (event: KeyboardEvent) => {
      if (isActive.value && event.key === 'Enter' && !props.disabled) {
        emit('keydown:enter')
      }
    }

    useEventListener('keydown', handleKeyDown)

    function onClick(e: MouseEvent) {
      emit('click', e)
    }

    function clickPrependHandler() {
      emit('click:prepend')
    }

    function clickAppendHandler() {
      emit('click:append')
    }

    function blurHandler() {
      emit('blur')
      isActive.value = false
      if (props.validateOn === 'blur') inputValidation()
    }

    function inputHandler() {
      emit('input')
      emit('update:modelValue', inputValue.value)
      if (props.validateOn === 'input') inputValidation()
    }

    function changeHandler() {
      emit('change')
      if (props.validateOn === 'change') inputValidation()
    }

    function submitHandler() {
      emit('submit')
      if (props.validateOn === 'submit') inputValidation()
    }

    function inputValidation() {
      const rulesFunctions = props.rules
      if (!rulesFunctions) return
      const isValid = rulesFunctions.every((rule) => {
        try {
          return rule(inputValue.value)
        } catch (error) {
          console.error(`Validation error: ${error}`)
          return false
        }
      })
      if (isValid) {
        emit('validationSuccess')
      } else {
        emit('validationError')
      }
    }

    const classes = computed(() => ({
      // eslint-disable-next-line max-len
      'overflow-x-auto box-border flex items-center gap-2 font-regular rounded-md border':
        true,
      'px-3': props.size === 'sm',
      'px-3.5': props.size === 'md' || props.size === 'lg',
      ...(isActive.value === false &&
        props.error === false &&
        !props.disabled === true && {
          'border-gray-300': true,
        }),
      ...(isActive.value === true &&
        props.error === false &&
        !props.disabled === true && {
          'border-primary-300 shadow-xs-btn shadow-primary-50': true,
        }),
      ...(isActive.value === true &&
        props.error === true &&
        !props.disabled === true && {
          'shadow-xs-btn shadow-error-50': true,
        }),
      ...(props.error === true &&
        !props.disabled === true && {
          'border-error-300': true,
        }),
      ...(!props.disabled === false && {
        'border-gray-300 bg-gray-50 cursor-not-allowed': true,
      }),
    }))

    const inputClasses = computed(() => ({
      // eslint-disable-next-line max-len
      'box-border min-w-[140px] w-full flex-grow bg-transparent font-regular text-gray-900 text-text-md':
        true,
      ...(!props.disabled === false && {
        'cursor-not-allowed': true,
      }),
      'py-[7px]': props.size === 'sm',
      'py-[9px]': props.size === 'md' || props.size === 'lg',
    }))

    const fieldClasses = computed(() => ({
      'box-border flex flex-col gap-1.5 w-full': true,
    }))

    const labelClasses = computed(() => ({
      'label font-medium text-gray-700 text-text-sm': true,
    }))

    const hintClasses = computed(() => ({
      'font-regular text-gray-600 text-text-sm': true,
    }))

    const errorMessageClasses = computed(() => ({
      'font-regular text-error-500 text-text-sm': true,
    }))

    const appendIconColor = computed(() => {
      if (!props.error) {
        return 'gray-400'
      }
      return 'error-500'
    })

    const errorsList = props.errorMessages?.map((item, index) => {
      return (
        <div class={errorMessageClasses.value} data-key={index}>
          {item}
        </div>
      )
    })

    useRender(() => (
      <div class={[fieldClasses.value, props.class]} onClick={onClick}>
        {hasLabel ? <div class={labelClasses.value}>{props.label}</div> : null}
        <div class={classes.value}>
          {hasPrepend ? (
            <div onClick={clickPrependHandler}>
              <UIcon
                color="gray-500"
                size="20"
                icon={`$${prependIcon.value}`}
                isActive={isActive.value}
                isHover={isHover.value}
              ></UIcon>
            </div>
          ) : null}
          {slots.default?.()}
          {
            <input
              v-model={inputValue.value}
              ref="inputElementRef"
              placeholder={props.placeholder}
              class={inputClasses.value}
              readonly={props.disabled}
              name={props.name}
              style="
                outline: 0;
                border: 0;
                --tw-ring-offset-width: 0;
                -webkit-transition: background-color 9999s ease-out;
                -webkit-transition-delay: 9999s;"
              type={props.type}
              autocomplete={props.autocomplete ? 'on' : 'off'}
              onFocus={() => (isActive.value = true)}
              onBlur={blurHandler}
              onInput={inputHandler}
              onChange={changeHandler}
              onSubmit={submitHandler}
            />
          }
          {hasAppend && (props.tooltipTitle || props.tooltipText) ? (
            <UTooltip
              onClick={clickAppendHandler}
              theme={props.tooltipTheme}
              position={props.tooltipPosition}
              class="w-fit h-fit"
            >
              {{
                tooltipToggle: () => (
                  <UIcon
                    color={appendIconColor.value}
                    size="16"
                    icon={`$${appendIcon.value}`}
                    class="mx-auto"
                  ></UIcon>
                ),
                text: () => <>{props.tooltipTitle}</>,
                supportingText: () => <>{props.tooltipText}</>,
              }}
            </UTooltip>
          ) : null}
          {hasAppend && !props.tooltipTitle ? (
            <UIcon
              color={appendIconColor.value}
              size="16"
              icon={`$${appendIcon.value}`}
              class="mx-auto"
              onClick={clickAppendHandler}
            ></UIcon>
          ) : null}
        </div>
        {!props.error && props.hint ? (
          <div class={hintClasses.value}>{props.hint}</div>
        ) : null}
        {props.error && props.errorMessages ? (
          <div class="error">{errorsList}</div>
        ) : null}
      </div>
    ))
    return {
      isActive,
    }
  },
})

export type UInput = InstanceType<typeof UInput>
