<script setup lang="ts">
import { computed, getCurrentInstance, useAttrs, type Slot } from 'vue'
import { useHasSlot } from '../../../composables/useHasSlots'
import type { FdsRadioProps } from './types'

defineOptions({
  inheritAttrs: false,
})

const modelValue = defineModel<string | number | boolean | null>()
const instance = getCurrentInstance()

// defineModel includes `boolean`, so Vue casts an omitted v-model to `false`
// instead of `undefined`. Detect a real parent binding from the vnode so
// :checked consumers still work.
const isUsingVModel = () => {
  const rawProps = instance?.vnode.props
  return !!(rawProps && ('modelValue' in rawProps || 'onUpdate:modelValue' in rawProps))
}

const props = withDefaults(defineProps<FdsRadioProps>(), {
  label: undefined,
  checked: false,
  value: undefined,
  disabled: false,
  required: false,
  id: undefined,
  name: undefined,
  class: undefined,
  dataTestid: undefined,
  inputClass: undefined,
  disablePseudoStates: false,
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: string | number | boolean | null): void
  (e: 'update:checked', value: boolean): void
  (e: 'change', value: string | number | boolean | null): void
  (e: 'input', value: string | number | boolean | null): void
}>()

const attrs = useAttrs()

const autoId = `fds-radio-${Math.random().toString(36).slice(2, 9)}`
const inputId = computed(() => props.id ?? (attrs.id as string | undefined) ?? autoId)

const labelAttrs = computed(() => {
  const eventHandlers: Record<string, unknown> = {}
  for (const key in attrs) {
    if (key.startsWith('on') && typeof attrs[key] === 'function') {
      eventHandlers[key] = attrs[key]
    }
  }
  return eventHandlers
})

const inputAttrs = computed(() => {
  const { class: _, id: _id, name: _name, disabled: _disabled, ...rest } = attrs
  const filtered: Record<string, unknown> = {}
  for (const key in rest) {
    if (!key.startsWith('on') || typeof rest[key] !== 'function') {
      filtered[key] = rest[key]
    }
  }
  return {
    ...filtered,
    id: inputId.value,
    name: props.name ?? (attrs.name as string | undefined),
    'data-testid': props.dataTestid ?? undefined,
  } as any
})

const hasLabelSlot = useHasSlot()

const externalClass = computed(() => props.class ?? attrs.class)

// Computed checked state for the input element
const isChecked = computed(() => {
  if (!isUsingVModel()) {
    // Using :checked prop mode - read from props directly
    return (props as any).checked ?? false
  }
  // Using v-model mode - check if this radio's value matches modelValue
  if (props.value !== undefined && props.value !== null) {
    return modelValue.value === props.value
  }
  return false
})

// Handle change events
function handleChange(e: Event) {
  const target = e.target as HTMLInputElement
  const val = props.value

  if (!isUsingVModel()) {
    // Using :checked prop mode
    if (target.checked && val !== undefined && val !== null) {
      emit('update:checked', true)
      emit('change', val)
      emit('input', val)
    } else {
      emit('update:checked', false)
      emit('change', val ?? false)
    }
  } else if (target.checked && val !== undefined && val !== null) {
    // Using v-model mode
    emit('update:modelValue', val)
    emit('change', val)
    emit('input', val)
  }
}

const wrapperClasses = computed(() => ['block relative flex items-center mb-2 last:mb-0'])
const innerWrapperClasses = computed(() => [
  'flex p-0.5 items-start rounded-l-xl rounded-r-md',
  !props.disablePseudoStates && 'hover:bg-blue_t-100 active:bg-blue_t-200',
  props.disablePseudoStates
    ? '[&:has(:focus)]:outline-none [&:has(:focus-visible)]:outline-none'
    : '[&:has(:focus-visible)]:outline-2 [&:has(:focus-visible)]:outline-dashed [&:has(:focus-visible)]:-outline-offset-2 [&:has(:focus-visible)]:outline-blue-500',
  props.disabled && 'hover:bg-transparent active:bg-transparent',
])

const inputClasses = computed(() => [
  'peer z-2 bg-white min-w-[20px] min-h-[20px] rounded-full accent-blue-500',
  props.disablePseudoStates
    ? 'focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0'
    : 'focus-visible:outline-none',
  props.disabled && 'cursor-not-allowed',
  props.inputClass,
])

defineSlots<{
  default?: Slot
}>()
</script>

<template>
  <div :class="[wrapperClasses, externalClass]">
    <label :for="inputId" :class="[innerWrapperClasses, { 'cursor-not-allowed': props.disabled }]" v-bind="labelAttrs">
      <input
        v-bind="inputAttrs"
        :value="props.value"
        :checked="isChecked"
        @change="handleChange"
        :disabled="props.disabled"
        :required="props.required"
        type="radio"
        :class="inputClasses"
        class="m-[2px]"
      />
      <span
        v-if="hasLabelSlot || props.label"
        class="relative inline-block leading-6 pl-1 select-none"
        :class="{ 'cursor-not-allowed': props.disabled }"
      >
        <template v-if="hasLabelSlot">
          <slot></slot>
        </template>
        <template v-else-if="props.label">
          {{ props.label }}
        </template>
      </span>
    </label>
  </div>
</template>
