<template>
  <div :class="wrapperClasses">
    <div class="relative">
      <input
        :id="inputId"
        v-model="modelValue"
        :type="type"
        :name="name"
        :placeholder="!hasFloatingLabel ? placeholder : ''"
        :disabled="disabled"
        :readonly="readonly"
        :required="required"
        :class="inputClasses"
        :aria-invalid="hasError"
        :aria-describedby="hasError ? `${inputId}-error` : undefined"
        @focus="handleFocus"
        @blur="handleBlur"
        @input="handleInput"
        @change="handleChange"
      />

      <label
        v-if="hasFloatingLabel"
        :for="inputId"
        :class="floatingLabelClasses"
      >
        {{ label }}<span v-if="required" class="text-red-500 ml-1">*</span>
      </label>
    </div>

    <Transition name="error-fade">
      <div
        v-if="hasError && errorMessage"
        :id="`${inputId}-error`"
        class="mt-1 text-sm text-red-600"
        role="alert"
        aria-live="polite"
      >
        {{ errorMessage }}
      </div>
    </Transition>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, useId } from 'vue'
import type { InputProps, InputEmits } from '@/types/components'

const props = withDefaults(defineProps<InputProps>(), {
  type: 'text',
  disabled: false,
  readonly: false,
  required: false,
  error: false,
  size: 'md',
  variant: 'default'
})

const emit = defineEmits<InputEmits>()

// Generate unique ID for the input
const inputId = useId()

// Reactive state
const isFocused = ref(false)
const hasValue = computed(() => props.modelValue != null && props.modelValue !== '')
const hasError = computed(() => props.error || !!props.errorMessage)
const hasFloatingLabel = computed(() => !!props.label)

// Handle v-model updates
const modelValue = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value as string | number)
})

// Event handlers
const handleFocus = (event: FocusEvent) => {
  isFocused.value = true
  emit('focus', event)
}

const handleBlur = (event: FocusEvent) => {
  isFocused.value = false
  emit('blur', event)
}

const handleInput = (event: Event) => {
  const target = event.target as HTMLInputElement
  emit('input', event)
  emit('update:modelValue', target.value)
}

const handleChange = (event: Event) => {
  emit('change', event)
}

// Dynamic classes
const wrapperClasses = computed(() => {
  const classes = ['titan-input-wrapper', 'relative']

  if (props.class) {
    classes.push(props.class)
  }

  return classes.join(' ')
})

const inputClasses = computed(() => {
  const classes = [
    // Base styles
    'w-full',
    'border-b-2',
    'bg-transparent',
    'pb-2',
    'text-base',
    'leading-6',
    'transition-all',
    'duration-200',
    'ease-in-out',
    'outline-none',
    'appearance-none',

    // Typography - inherit from parent
    'font-medium',

    // Focus styles
    'focus:outline-none',
  ]

  // Size variants with floating label considerations
  if (hasFloatingLabel.value) {
    // When we have a floating label, use fixed top padding for label space
    classes.push('pt-6')

    switch (props.size) {
      case 'sm':
        classes.push('pb-2', 'text-sm')
        break
      case 'lg':
        classes.push('pb-4', 'text-lg')
        break
      default: // md
        classes.push('pb-3', 'text-base')
    }
  } else {
    // No floating label, use normal padding
    switch (props.size) {
      case 'sm':
        classes.push('py-2', 'text-sm')
        break
      case 'lg':
        classes.push('py-4', 'text-lg')
        break
      default: // md
        classes.push('py-3', 'text-base')
    }
  }

  // State-based styling
  if (hasError.value) {
    classes.push('border-red-500', 'focus:border-red-600')
  } else {
    classes.push(
      'border-current',
      'opacity-30',
      'focus:border-titan-green-800',
      'focus:opacity-100',
      'hover:opacity-50'
    )
  }

  if (props.disabled) {
    classes.push(
      'opacity-50',
      'cursor-not-allowed',
      'bg-gray-50'
    )
  } else if (props.readonly) {
    classes.push(
      'cursor-default'
    )
  }

  return classes.join(' ')
})

const floatingLabelClasses = computed(() => {
  const classes = [
    'absolute',
    'left-0',
    'transition-all',
    'duration-200',
    'ease-in-out',
    'origin-left',
    'pointer-events-none',
    'select-none',
  ]

  // Floating state
  const shouldFloat = isFocused.value || hasValue.value || !!props.placeholder

  if (shouldFloat) {
    classes.push(
      'text-xs',
      'font-medium',
      'top-0',
      'scale-90'
    )

    // Color when floating
    if (hasError.value) {
      classes.push('text-red-600')
    } else if (isFocused.value) {
      classes.push('text-titan-green-800')
    } else {
      classes.push('opacity-70')
    }
  } else {
    classes.push(
      'text-base',
      'font-normal',
      'scale-100'
    )

    // Position label exactly where native placeholder would appear
    // Native placeholder aligns with text baseline = pt-6 padding
    classes.push('top-6')

    // Color when not floating
    if (hasError.value) {
      classes.push('text-red-500')
    } else {
      classes.push('opacity-60')
    }
  }

  return classes.join(' ')
})
</script>

<style scoped>
.error-fade-enter-active,
.error-fade-leave-active {
  transition: all 0.2s ease-in-out;
}

.error-fade-enter-from {
  opacity: 0;
  transform: translateY(-4px);
}

.error-fade-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}
</style>