<template>
  <div :class="mergedGroupClass">
    <textarea
      :id="inputId"
      :name="name"

      :autocomplete="autocomplete"
      class="floating-input peer w-full border bg-white pl-3 pr-3 py-1 outline-none placeholder:text-transparent overflow-auto"
      :class="[
      isFilled ? 'border-black' : 'border-m-muted',
      disabled ? 'cursor-not-allowed text-black/60 border-m-muted' : 'cursor-text',
      hasError
        ? 'border-m-danger focus:border-m-danger'
        : hasSuccess
          ? 'border-m-success focus:border-m-success'
          : 'focus:border-m-primary',
      textInput,
      showCounterComputed ? 'pb-6' : '',
      rounded,
    ]"
      :required="required"
      :maxlength="maxLength"
      :rows="rowsCount"
      :disabled="disabled"
      :value="currentValue"
      :readonly="readonly"
      :aria-invalid="hasError"
      :aria-describedby="describedBy"
      :style="textareaStyle"
      v-bind="attrs"
      placeholder="_"
      @input="onInput"
      @focus="isFocused = true"
      @blur="isFocused = false"
    />
    <label
      v-if="label"
      :for="inputId"
      class="floating-label absolute left-3 top-2 mt-1 inline-block origin-left transition-transform duration-150 font-medium"
      :class="[
    shouldFloatLabel ? '-translate-y-[1.30rem] scale-90' : '',
    disabled ? 'text-black/60' : '',
    hasError
      ? 'text-m-danger'
      : hasSuccess
        ? 'text-m-success'
        : isFocused ? 'text-m-primary' : shouldFloatLabel ? 'text-black' : 'text-m-muted',
      textLabel,
      ]"
    >
      {{ label }}
    </label>
    <span
      v-if="showCounterComputed"
      class="pointer-events-none absolute bottom-2 left-3 text-xs text-m-muted"
    >
      {{ currentLength }}/{{ maxLength }}
    </span>
  </div>
  <div
    v-if="hasError || hasSuccess || hint"
    class="mt-1 flex items-center justify-between gap-2 text-xs"
  >
    <p
      :id="`${inputId}-describedby`"
      :class="[
        hasError
          ? 'text-m-danger'
          : hasSuccess
            ? 'text-m-success'
            : 'text-m-muted',
        'ml-[2px]',
      ]"
    >
      {{ error || success || hint }}
    </p>
  </div>
</template>

<script setup lang="ts">
import {computed, ref, useAttrs, useId} from 'vue'
import {twMerge} from 'tailwind-merge'

defineOptions({name: 'MalioInputTextArea', inheritAttrs: false})

const props = withDefaults(
  defineProps<{
    id?: string
    label?: string
    name?: string
    autocomplete?: string
    modelValue?: string | null | undefined
    size?: number | string
    textInput?: string
    textLabel?: string
    resize?: 'none' | 'both' | 'horizontal' | 'vertical'
    minResizeWidth?: number
    maxResizeWidth?: number
    minResizeHeight?: number
    maxResizeHeight?: number
    required?: boolean
    maxLength?: number
    showCounter?: boolean
    disabled?: boolean
    readonly?: boolean
    hint?: string
    error?: string
    success?: string
    rounded?: string
    groupClass?: string

  }>(),
  {
    id: '',
    name: '',
    autocomplete: 'off',
    modelValue: undefined,
    label: '',
    size: 2,
    textInput: 'text-lg',
    required: false,
    maxLength: 800,
    showCounter: false,
    readonly: false,
    textLabel: 'text-sm',
    disabled: false,
    rounded: 'rounded-md',
    hint: '',
    error: '',
    success: '',
    resize: 'both',
    minResizeWidth: 280,
    maxResizeWidth: 640,
    minResizeHeight: 40,
    maxResizeHeight: 320,
    groupClass: '',
  },
)

const mergedGroupClass = computed(() =>
  twMerge('relative w-full', props.groupClass),
)

const attrs = useAttrs()
const generatedId = useId()
const localValue = ref('')
const isFocused = ref(false)

const inputId = computed(() => props.id?.toString() || `malio-input-textarea-${generatedId}`)
const isControlled = computed(() => props.modelValue !== undefined)
const currentValue = computed(() => (isControlled.value ? (props.modelValue ?? '') : localValue.value))
const shouldFloatLabel = computed(() => isFocused.value || currentValue.value.length > 0)
const hasError = computed(() => !!props.error)
const hasSuccess = computed(() => !!props.success && !hasError.value)
const rowsCount = computed(() => Math.max(1, Number(props.size || 3)))
const currentLength = computed(() => (currentValue.value ?? '').length)
const showCounterComputed = computed(() =>
  props.showCounter && Number(props.maxLength) > 0
)
const toCssSize = (value: number | string) => (typeof value === 'number' ? `${value}px` : value)
const textareaStyle = computed(() => ({
  resize: props.resize,
  minWidth: toCssSize(props.minResizeWidth),
  maxWidth: toCssSize(props.maxResizeWidth),
  minHeight: toCssSize(props.minResizeHeight),
  maxHeight: toCssSize(props.maxResizeHeight),
}))
const isFilled = computed(() => currentValue.value.trim().length > 0)
const describedBy = computed(() =>
  (hasError.value || hasSuccess.value || !!props.hint) ? `${inputId.value}-describedby` : undefined,
)

const emit = defineEmits<{
  (event: 'update:modelValue', value: string): void
}>()

const onInput = (event: Event) => {
  const target = event.target as HTMLTextAreaElement
  if (!isControlled.value) {
    localValue.value = target.value
  }
  emit('update:modelValue', target.value)
}
</script>

<style scoped>
.floating-label {
  background: white;
  padding: 0 0.25rem;
}
</style>
