import {
  computed, type ComputedRef, defineComponent, type PropType, ref, type Ref, watch, onMounted,
} from 'vue'
import { copyTooltip } from '../../helpers'

import './styles.scss'
import '../../styles/styles.scss'

import EyeHide from './assets/eye-hide'
import EyeShow from './assets/eye-show'

import BlackTooltip from '../tooltips/BlackTooltip.vue'

/**
 * Поле ввода данных. Дает возможность указать значение с помощью клавиатуры. Предназначен для короткого однострочного содержимого.
 *
 * <ul>
 *   <li><a href="https://www.figma.com/file/Q4Z4CnFx4i0qhD5fbgnPpf/%F0%9F%92%BB-%D0%94%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-ARK-SPACE?type=design&node-id=11938%3A133128&mode=design&t=0u1YOOSjcXlPTQHn-1" target="_blank">Дизайн</a></li>
 * </ul>
 */
export default defineComponent({
  props: {
    /**
     * Помечает поле обязательным \
     * Так же добавляет звездочку (`*`) рядом с названием поля
     */
    isRequired: {
      type: Boolean,
      default: false,
    },

    /**
     * Помечает поле заблокированным \
     * Так же скрывает ошибки, отображаемые снизу поля
     */
    isDisabled: {
      type: Boolean,
      default: false,
    },

    /**
     * Делает поле числовым типом
     */
    isForNumber: {
      type: Boolean,
      default: false,
    },

    /**
     * Делает поле подходящим для использования паролей \
     * Добавляет кнопку показа/скрытия текста в поле
     */
    isForPassword: {
      type: Boolean,
      default: false,
    },

    /**
     * Позволяет копировать текст поля при клике по нему
     */
    isWithCopy: {
      type: Boolean,
      default: false,
    },

    /**
     * Позволяет отображать `tooltip` (подсказку) рядом с полем \
     * Подсказка отображается только тогда, когда текст в поле выходит за его рамки
     */
    isShowTooltip: {
      type: Boolean,
      default: false,
    },

    /**
     * Сброс значения
     */
    isResettable: {
      type: Boolean,
      default: false,
    },

    /**
     * Название поля, отображающееся сверху него
     */
    label: {
      type: String,
      default: '',
    },

    /**
     * Позволяет установить максимальную длину символов в тексте поля
     */
    maxLength: {
      type: Number,
    },

    /**
     * Полупрозрачный текст, отображающийся, когда значение поля пустое
     */
    placeholder: {
      type: String,
      default: '',
    },

    /**
     * Описание поля, отображающееся под полем
     */
    description: {
      type: String,
      default: '',
    },

    /**
     * Отображение пользовательского элемента слева
     */
    leftSlot: {
      type: Function as PropType<() => JSX.Element>,
      default: undefined,
    },

    /**
     * Отображение пользовательского элемента справа
     */
    rightSlot: {
      type: Function as PropType<() => JSX.Element>,
      default: undefined,
    },

    /**
     * Значение поля
     */
    modelValue: {
      type: String,
      default: '',
    },

    /**
     * Ошибки поля \
     * Можно передать массив строк, для отображения нескольких ошибок, либо передать строку, для отображения одной ошибки
     */
    errors: {
      type: [Array, String] as PropType<string[] | string>,
      default: () => [],
    },

    /**
     * Хук, запускающийся после ввода символа в поле \
     * Принимает в себя событие `DOM` хука `onInput`
     *
     * ! Внимание: при его определении, необходимо вручную менять значение поля, потому что пропс `onValueChange` не используется
     */
    onInput: {
      type: Function as PropType<(e: Event) => void>,
    },

    /**
     * Хук, запускающийся после активации фокуса в поле \
     * Принимает в себя событие `DOM` хука `onFocusin`
     */
    onFocusIn: {
      type: Function as PropType<(e: Event) => void>,
      default: () => {
        // do nothing
      },
    },

    /**
     * Хук, запускающийся после деактивации фокуса в поле \
     * Принимает в себя событие `DOM` хука `onFocusout`
     */
    onFocusOut: {
      type: Function as PropType<(e: Event) => void>,
      default: () => {
        // do nothing
      },
    },

    /**
     * `Callback` для изменения значения в поле \
     * Принимает в себя новое значение, введенное в поле
     */
    onValueChange: {
      type: Function as PropType<(v: string) => void>,
    },

    /**
     * `Callback` для слежения и мутации значения в инпуте
     * Принимает текущее значение, введенное в поле
     */
    mask: {
      type: Function as PropType<(v: string) => string>,
    },
  },

  setup(props) {
    const isPassword: Ref<boolean> = ref(props.isForPassword)
    const isFieldValueMoreThanFieldWidth = ref(false)
    const modelValue = ref(props.modelValue)

    const element = ref(null) as unknown as Ref<HTMLInputElement>
    const type: ComputedRef<HTMLInputElement['type']> = computed(() => {
      if (isPassword.value) return 'password'
      if (props.isForNumber) return 'number'
      return 'text'
    })

    const changeIsPassword = (value: boolean) => {
      isPassword.value = value
    }

    const inputHandler = (e: Event) => {
      const element = (e.target as HTMLInputElement)
      const text = element.value
      if (props.mask) element.value = text
      modelValue.value = props.mask ? props.mask(text) : text
    }

    const copy = (text: string, event: MouseEvent) => {
      if (!props.isWithCopy) return

      copyTooltip(text, event.clientX + 10, event.clientY - 30)
    }

    const onFocusIn = (e: FocusEvent) => {
      props.onFocusIn(e)

      setTimeout(() => {
        const $input = e.target as HTMLInputElement
        $input.selectionStart = $input.value.length
        $input.scrollLeft = $input?.scrollWidth
      }, 100)
    }

    const checkInputSize = () => {
      isFieldValueMoreThanFieldWidth.value = element.value?.scrollWidth > element.value?.clientWidth
    }

    const onFocusOut = (e: FocusEvent) => {
      props.onFocusOut(e)

      checkInputSize()
    }

    watch(() => props.modelValue, (value) => {
      modelValue.value = value
    })

    watch(modelValue, (value) => {
      if (props.mask) modelValue.value = props.mask(value)
      if (props.onValueChange) props.onValueChange(modelValue.value)
    })

    onMounted(checkInputSize)

    return (): JSX.Element => {
      const inputElement = () => (
        <label class="ark-ui-input" onClick={(event) => copy(props.modelValue, event)}>
          {props.leftSlot && (
            <div class="ark-ui-input-leftSlot">
              {props.leftSlot()}
            </div>
          )}

          <input
            ref={element}
            class="ark-ui-input-element"
            onInput={props.onInput ? props.onInput : inputHandler}
            onFocusin={onFocusIn}
            onFocusout={onFocusOut}
            value={modelValue.value}
            type={type.value}
            required={props.isRequired}
            placeholder={props.placeholder}
            disabled={props.isDisabled}
            maxlength={props.maxLength}
          />

          {props.rightSlot && (
            <div class="ark-ui-input-rightSlot">
              {props.rightSlot()}
            </div>
          )}

          {props.isResettable && (
            <div class="ark-ui-input-reset" onClick={() => { modelValue.value = '' }}>
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                {/* eslint-disable-next-line max-len */}
                <path d="M4.14645 4.14645C4.34171 3.95118 4.65829 3.95118 4.85355 4.14645L8 7.29289L11.1464 4.14645C11.3417 3.95118 11.6583 3.95118 11.8536 4.14645C12.0488 4.34171 12.0488 4.65829 11.8536 4.85355L8.70711 8L11.8536 11.1464C12.0488 11.3417 12.0488 11.6583 11.8536 11.8536C11.6583 12.0488 11.3417 12.0488 11.1464 11.8536L8 8.70711L4.85355 11.8536C4.65829 12.0488 4.34171 12.0488 4.14645 11.8536C3.95118 11.6583 3.95118 11.3417 4.14645 11.1464L7.29289 8L4.14645 4.85355C3.95118 4.65829 3.95118 4.34171 4.14645 4.14645Z" fill="#27282E"/>
              </svg>

            </div>
          )}

          { props.isForPassword && (
            <div onClick={() => changeIsPassword(!isPassword.value)} class="ark-ui-input-icon">
              { isPassword.value ? <EyeHide /> : <EyeShow /> }
            </div>
          ) }
        </label>
      )

      return (
        <div class={`ark-ui-input-box ${props.errors.length ? 'with-errors' : ''} ${props.isWithCopy ? 'withCopy' : ''} ${props.isDisabled ? 'disabled' : ''}`}>
          {props.label && (
            <div class="ark-ui-title ark-ui-label">{ props.label }{ props.isRequired ? '*' : '' }</div>
          )}

          {
            props.isShowTooltip && isFieldValueMoreThanFieldWidth.value && props.modelValue
              ? (
                <BlackTooltip text={props.modelValue} class="ark-ui-input-tooltip">
                  { inputElement() }
                </BlackTooltip>
              )
              : inputElement()
          }

          { !props.isDisabled && (
            <>
              { typeof props.errors === 'string' && props.errors.length > 0
                ? <div class="ark-ui-title ark-ui-error">{ props.errors }</div>
                : Array.isArray(props.errors) && props.errors.length > 0
                  ? props.errors.map((error) => <div class="ark-ui-title ark-ui-error">{ error }</div>)
                  : props.description
                    ? <div class="ark-ui-title ark-ui-description">{ props.description }</div>
                    : '' }
            </>
          ) }
        </div>
      )
    }
  },
})
