<template>
  <div :class="mergedGroupClass">
    <span
      v-if="label"
      :id="`${groupId}-label`"
      :class="mergedLabelClass"
    >
      {{ label }}<MalioRequiredMark v-if="required" />
    </span>

    <div
      role="radiogroup"
      :aria-labelledby="label ? `${groupId}-label` : undefined"
      :aria-invalid="hasError || undefined"
      :aria-describedby="describedBy"
      :class="contentZoneClass"
    >
      <MalioRadioButton
        v-for="(option, index) in options"
        :key="`${groupId}-opt-${index}`"
        :label="option.label"
        :value="option.value"
        :disabled="option.disabled"
        :input-class="inputClass"
      />
      <slot />
    </div>

    <p
      v-if="reserveMessageSpace || hint || error || success"
      :id="`${groupId}-describedby`"
      :class="[
        hasError
          ? 'text-m-danger'
          : hasSuccess
            ? 'text-m-success'
            : 'text-m-muted',
        'mt-1 ml-[2px] text-xs',
        reserveMessageSpace ? 'min-h-[1rem]' : '',
      ]"
    >
      {{ error || success || hint }}
    </p>
  </div>
</template>

<script setup lang="ts">
import {computed, provide, ref, useId} from 'vue'
import {twMerge} from 'tailwind-merge'
import MalioRadioButton from './RadioButton.vue'
import MalioRequiredMark from '../shared/RequiredMark.vue'
import {radioGroupContextKey, type RadioValue} from './context'

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

interface RadioOption {
  label: string
  value: RadioValue
  disabled?: boolean
}

const props = withDefaults(
  defineProps<{
    modelValue?: RadioValue
    options?: RadioOption[]
    label?: string
    name?: string
    inline?: boolean
    disabled?: boolean
    readonly?: boolean
    required?: boolean
    hint?: string
    error?: string
    success?: string
    reserveMessageSpace?: boolean
    groupClass?: string
    contentClass?: string
    inputClass?: string
    labelClass?: string
  }>(),
  {
    modelValue: undefined,
    options: () => [],
    label: '',
    name: '',
    inline: false,
    disabled: false,
    readonly: false,
    required: false,
    hint: '',
    error: '',
    success: '',
    reserveMessageSpace: true,
    groupClass: '',
    contentClass: '',
    inputClass: '',
    labelClass: '',
  },
)

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

const generatedId = useId()
const groupId = computed(() => props.name || `malio-radio-group-${generatedId}`)

const localValue = ref<RadioValue>(undefined)
const isControlled = computed(() => props.modelValue !== undefined)
const selectedValue = computed(() =>
  isControlled.value ? props.modelValue : localValue.value,
)

const hasError = computed(() => !!props.error)
const hasSuccess = computed(() => !!props.success && !hasError.value)
const shouldShowMessage = computed(() => !!(props.hint || hasError.value || hasSuccess.value))
const describedBy = computed(() =>
  props.reserveMessageSpace || shouldShowMessage.value
    ? `${groupId.value}-describedby`
    : undefined,
)

const select = (value: RadioValue) => {
  if (props.readonly || props.disabled) return
  if (!isControlled.value) localValue.value = value
  emit('update:modelValue', value)
}

provide(radioGroupContextKey, {
  name: computed(() => groupId.value),
  isSelected: (value: RadioValue) => selectedValue.value === value,
  select,
  hasError,
  hasSuccess,
  disabled: computed(() => props.disabled),
  readonly: computed(() => props.readonly),
  required: computed(() => props.required),
  describedBy,
})

const contentZoneClass = computed(() =>
  twMerge(
    props.inline
      ? 'flex flex-wrap items-center gap-x-6 min-h-[3rem]'
      : 'flex flex-col gap-y-1',
    props.contentClass,
  ),
)

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

const mergedLabelClass = computed(() =>
  twMerge(
    'mb-1 block text-sm text-m-text',
    hasError.value ? 'text-m-danger' : '',
    props.labelClass,
  ),
)
</script>
