<template>
  <div
    class="nv-input"
    :class="[
      ...sizeableClasses,
      {
        'nv-input--prepend': $slots.prepend || prependIcon,
        'nv-input--append': $slots.append || appendIcon,
        'nv-input--number': type === 'number',
        'nv-input--clearable': clearable,
        'nv-input--disabled': computedDisabled,
      },
    ]"
  >
    <div class="nv-input__field">
      <div
        v-if="$slots['prepend-outer'] || prependOuterIcon"
        class="iconbox iconbox--outer iconbox--outer-prepend"
        ref="prependOuterIconBox"
      >
        <slot name="prepend-outer" />
        <div
          v-if="!$slots['prepend-outer'] && prependOuterIcon"
          class="iconbox__icon-wrap"
          @click="$emit('click:prependOuter')"
        >
          <NvIcon :icon="prependOuterIcon" />
        </div>
      </div>
      <div
        class="nv-input__field__inner nv-input__control"
        @click="clickedControl"
        @mouseover="hovered"
        @mouseleave="unhovered"
        v-click-outside="blured"
      >
        <slot name="prependControl" />
        <fieldset
          class="nv-input__fieldset"
          :class="
            Array.isArray(fieldsetClasses)
              ? [...fieldsetClasses, getBorderColor]
              : [fieldsetClasses, getBorderColor]
          "
          :style="{
            height: `calc(100% + ${legendHeight / 2}px)`,
            top: `${-legendHeight / 2}px`,
          }"
        >
          <legend
            v-if="label"
            ref="legend"
            class="nv-input__legend"
            :class="{ '!w-0 !px-0': !activeLabel }"
            :style="{ 'max-width': `${input?.clientWidth - 24}px` }"
            v-text="label"
          />
          <!-- ToDo: сделать компонент Linear Progress и заменить на него -->
          <div v-if="loading" class="nv-input__loader">
            <div class="linear-activity">
              <div class="indeterminate"></div>
            </div>
          </div>
        </fieldset>
        <div class="h-full w-full flex items-start">
          <div
            v-if="$slots.prepend || prependIcon"
            class="iconbox iconbox--inner iconbox--inner-prepend"
            ref="prependIconBox"
          >
            <slot name="prepend" />
            <div
              v-if="!$slots.prepend && prependIcon"
              class="iconbox__icon-wrap"
              @click.stop="$emit('click:prepend')"
            >
              <NvIcon :icon="prependIcon" />
            </div>
          </div>
          <div class="flex grow h-full w-full items-start -mt-px relative">
            <div class="flex grow w-full h-full items-center">
              <slot name="prependInput" />
              <component
                :is="fieldType"
                ref="input"
                :disabled="computedDisabled"
                :readonly="computedReadonly"
                :value="modelValue"
                :type="type"
                :height="height"
                :placeholder="focus ? placeholder : ''"
                :min="min"
                :max="max"
                :step="step"
                @input="inputed"
                @change="handleChange"
                class="nv-input__input"
                :class="compInputClasses"
                :style="{
                  'min-height': `${
                    autoGrow
                      ? Math.max(parseInt(height) || 0, input?.scrollHeight) +
                        'px'
                      : ''
                  }`,
                  resize:
                    type === 'input'
                      ? 'none'
                      : autoGrow || noResize
                      ? 'none'
                      : 'vertical',
                }"
                :rows="rows"
              />
            </div>
            <label
              v-if="label"
              class="nv-input__label"
              :class="[
                activeLabel ? 'px-1' : '',
                getTextColor,
                { 'nv-input__label--active': activeLabel },
              ]"
              :style="{
                left: activeLabel ? `${-prependIconBox?.clientWidth}px` : 0,
                top: activeLabel ? `${-legendHeight / 2}px` : 0,
              }"
              v-text="label"
            />
          </div>
          <div
            v-if="$slots.append || appendIcon || (clearable && modelValue)"
            ref="appendIconBox"
            class="iconbox iconbox--inner iconbox--inner-append"
            :style="{
              right:
                !hideSpinButtons && input?.scrollHeight > input?.clientHeight
                  ? '20px'
                  : 0,
            }"
          >
            <NvIcon
              v-if="
                clearable &&
                modelValue &&
                !computedDisabled &&
                !computedReadonly
              "
              class="nv-input__close"
              icon="nvi-close"
              @click.stop="cleared"
            />
            <div>
              <slot name="append" />
              <div
                v-if="!$slots.append && appendIcon"
                class="iconbox__icon-wrap"
                @click.stop="$emit('click:append')"
              >
                <NvIcon :icon="appendIcon" />
              </div>
            </div>
          </div>
        </div>
      </div>
      <div
        v-if="$slots['append-outer'] || appendOuterIcon"
        class="iconbox iconbox--outer iconbox--outer-append"
        ref="appendOuterIconBox"
      >
        <slot name="append-outer" />
        <div
          v-if="!$slots['append-outer'] && appendOuterIcon"
          class="iconbox__icon-wrap"
          @click="$emit('click:appendOuter')"
        >
          <NvIcon :icon="appendOuterIcon" />
        </div>
      </div>
    </div>

    <NvFieldDetails
      v-bind="inputDetailsProps"
      :size="size"
      :counterValue="modelValue?.length"
      class="mt-2"
      :style="{
        'margin-left': `${prependOuterIconBox?.clientWidth}px `,
        'margin-right': `${appendOuterIconBox?.clientWidth}px `,
      }"
    />
  </div>
</template>

<script>
import { computed, inject, onMounted, ref } from "vue";
import useSize from "../../composables/sizeable";
import { useGetFieldDetailsProps } from "../../composables/fieldDetails";
import { useAddTextareaProps, useInputProps, useAddInputNumberProps } from "../../composables/input";
import { NvFieldDetails } from "../NvFieldDetails";
import { NvIcon } from "../NvIcon";
import { ClickOutside } from "../../directives";

export default {
  props: {
    ...useInputProps,
    fieldType: {
      type: String,
      default: "input",
      validator: (value) => ["input", "textarea"].includes(value),
    },
    fieldsetClasses: [String, Array, Object],
    inputClasses: [String, Array, Object],
    //--- Textarea
    ...useAddTextareaProps,
    //--- Input Number
    ...useAddInputNumberProps
  },
  components: { NvFieldDetails, NvIcon },
  directives: { ClickOutside },
  // ToDo: прописать эти эмиты в документации
  emits: [
    "change",
    "update:modelValue",
    "click:control",
    "click:prependOuter",
    "click:prepend",
    "click:appendOuter",
    "click:clear",
    "click:append",
    "focus",
    "blur",
  ],

  setup(props, { emit }) {
    // Size
    const { sizeableClasses } = useSize(props);

    const parentDisabled = inject("parentDisabled", undefined);
    const computedDisabled = computed(() => parentDisabled || props.disabled);

    const parentReadonly = inject("parentReadonly", undefined);
    const computedReadonly = computed(() => parentReadonly || props.readonly);

    // InputDetailsProps
    const inputDetailsProps = computed(() => useGetFieldDetailsProps(props));

    const legend = ref(null);
    let legendHeight = ref(0);
    onMounted(() => {
      // Костыль для правильной высоты
      setTimeout(() => {
        setTimeout(() => {
          legendHeight.value = legend.value?.clientHeight;
        }, 0);
      }, 0);
    });

    const focus = ref(false);
    const hover = ref(false);

    const getBorderColor = computed(() => {
      if (hover.value || focus.value) return "border-primary";
      if (props.error || !!props.messagesError.length) return "border-alert";
      if (props.success || !!props.messagesSuccess.length)
        return "border-success";
      return "border-gray-300";
    });

    const getTextColor = computed(() => {
      if (computedDisabled.value) return "text-subline";
      if (hover.value || focus.value) return "text-primary";
      if ((props.error || !!props.messagesError.length) && props.modelValue)
        return "text-alert";
      if ((props.success || !!props.messagesSuccess.length) && props.modelValue)
        return "text-success";
      return "text-subline";
    });

    // $refs
    const input = ref(null);
    const appendIconBox = ref(null);
    const appendOuterIconBox = ref(null);
    const prependIconBox = ref(null);
    const prependOuterIconBox = ref(null);

    const inputed = ($event) => {
      // input.value.style.height = "auto";
      emit("update:modelValue", $event.target.value);
    };

    const hovered = () => {
      hover.value = true;
    };

    const unhovered = () => {
      hover.value = false;
    };

    const clickedControl = () => {
      focused();
      emit("click:control");
    };

    const focused = () => {
      if (focus.value) return;

      if (input) input.value.focus();
      focus.value = true;
      emit("focus");
    };

    const blured = () => {
      if (!focus.value) return;

      if (input) input.value.blur();
      focus.value = false;
      emit("blur");
    };

    const cleared = () => {
      emit("click:clear");
      emit("update:modelValue", null);
      emit('change', null)
    };

    const handleChange = (event) => {
      emit('change', event.target.value)
    }

    const compInputClasses = computed(() => {
      const base = {
        "hide-spin-buttons": props.hideSpinButtons,
        "px-0":
          appendIconBox.value?.clientWidth +
          (!props.hideSpinButtons &&
            input.value?.scrollHeight <= input.value?.clientHeight),
      };
      return Array.isArray(props.inputClasses)
        ? [...props.inputClasses, base]
        : [props.inputClasses, base];
    });

    const activeLabel = computed(() => focus.value || props.modelValue);

    return {
      appendIconBox,
      appendOuterIconBox,
      computedDisabled,
      computedReadonly,
      getBorderColor,
      getTextColor,
      focus,
      input,
      legendHeight,
      prependIconBox,
      prependOuterIconBox,
      sizeableClasses,
      inputDetailsProps,
      activeLabel,
      legend,
      compInputClasses,
      blured,
      clickedControl,
      focused,
      hovered,
      unhovered,
      inputed,
      cleared,
      handleChange
    };
  },
};
</script>
