import '../../styles/styles.scss'
import {
  defineComponent, onMounted, onUnmounted, type PropType, ref, type Ref,
} from '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=13052%3A160086&mode=design&t=NMdOhanLgvl3DT10-1" target="_blank">Дизайн</a></li>
 * </ul>
 */
export default defineComponent({
  props: {
    /**
     * Позволяет автоматически изменять высоту компонента, если контент в нем не помещается
     */
    autoresize: {
      type: Boolean,
      default: false,
    },

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

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

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

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

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

    /**
     * Отключает возможность изменения размера компонента
     */
    disableResize: {
      type: Boolean,
      default: false,
    },

    /**
     * Значение поля
     */
    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` хука `onFocusout`
     */
    onFocusOut: {
      type: Function as PropType<(e: Event) => void>,
      default: () => {
        // do nothing
      },
    },

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

  setup(props) {
    let isResizing = false
    const isWithScroll = ref(false)

    let startResizeX = 0
    let startResizeY = 0
    let startResizeWidth = 0
    let startResizeHeight = 0

    const element: Ref<HTMLElement | undefined> = ref(undefined)
    const resizerElement: Ref<HTMLElement | undefined> = ref(undefined)

    const checkScroll = () => {
      if (!element.value) return

      isWithScroll.value = element.value.scrollHeight > element.value.clientHeight
    }

    const doResize = () => {
      if (element.value && props.autoresize) {
        if (element.value.scrollHeight > element.value.clientHeight) element.value.style.height = `${element.value.scrollHeight + 10}px`
      }
    }

    const inputHandler = (e: Event) => {
      if (props.onValueChange) {
        props.onValueChange((e.target as HTMLInputElement).value)
      }

      doResize()
    }

    const onInput = (e: Event) => {
      if (props.onInput) props.onInput(e)
      else inputHandler(e)

      checkScroll()
    }

    const resizeStart = (e: MouseEvent) => {
      e.preventDefault()

      if (!element.value) return

      isResizing = true
      startResizeX = e.clientX
      startResizeY = e.clientY
      startResizeWidth = element.value.offsetWidth
      startResizeHeight = element.value.offsetHeight
    }

    const resize = (e: MouseEvent) => {
      if (!isResizing || !element.value) return

      const deltaX = e.clientX - startResizeX
      const deltaY = e.clientY - startResizeY

      element.value.style.width = `${startResizeWidth + deltaX}px`
      element.value.style.height = `${startResizeHeight + deltaY}px`

      checkScroll()
    }

    const resizeEnd = () => {
      isResizing = false
    }

    onMounted(() => {
      doResize()
      checkScroll()

      window.addEventListener('resize', checkScroll)
      window.addEventListener('mousemove', resize)
      window.addEventListener('mouseup', resizeEnd) // На случай, если курсор мыши вышел из области `resizerElement`
    })

    onUnmounted(() => {
      window.removeEventListener('resize', checkScroll)
      window.removeEventListener('mousemove', resize)
      window.removeEventListener('mouseup', resizeEnd)
    })

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

        <label class={`ark-ui-textarea-wrapper ${isWithScroll.value ? 'scrollable' : ''}`}>
          <textarea
            ref={element}
            class={`ark-ui-textarea-element ${props.autoresize ? 'autoresize' : ''} ${props.disableResize ? 'disableResize' : ''}`}
            onInput={onInput}
            onFocusout={props.onFocusOut}
            value={props.modelValue}
            required={props.isRequired}
            placeholder={props.placeholder}
            disabled={props.isDisabled}
          />

          {!props.autoresize && !props.disableResize && (
            <div ref={resizerElement} class="ark-ui-textarea-resizer" onMousedown={resizeStart} onMouseup={resizeEnd}>
              <svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
                <rect width="11" height="11" fill="white" style="mix-blend-mode:multiply"/>
                <path class="ark-ui-textarea-resizer-icon" d="M6 9L9 6M2 9L9 2" stroke="currentColor" stroke-linecap="round"/>
              </svg>
            </div>
          )}
        </label>

        { !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>
    )
  },
})
