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

defineOptions({
  inheritAttrs: false,
})

// Support v-model as boolean (single checkbox) or array (checkbox group)
// Also support :checked prop for backward compatibility
const modelValue = defineModel<boolean | Array<string | number | boolean | null>>()
const instance = getCurrentInstance()

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

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

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

const { id, attrs } = useAttrsWithDefaults(props)

const autoId = `fds-checkbox-${Math.random().toString(36).slice(2, 9)}`
const inputId = computed(() => id.value ?? 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, ...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,
    'data-testid': props.dataTestid ?? undefined,
  } as any
})

const hasLabelSlot = useHasSlot()

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

const wrapperClasses = computed(() => ['block relative flex items-center mb-2 last:mb-0'])
const innerWrapperClasses = computed(() => [
  'flex p-0.5 items-start rounded-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(() => [
  'rounded-md transition-colors duration-200 accent-blue-500 m-[3px]',
  'peer z-2 bg-white min-w-[18px] min-h-[18px]',
  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,
])

// Bridge for v-model (boolean or array) or :checked prop
const internalChecked = computed({
  get: (): boolean => {
    if (!isUsingVModel()) {
      return props.checked ?? false
    }
    // If modelValue is an array, check if this checkbox's value is in it
    if (Array.isArray(modelValue.value)) {
      const val = props.value
      if (val === undefined || val === null) return false
      return modelValue.value.includes(val)
    }
    // Otherwise treat as boolean
    return Boolean(modelValue.value)
  },
  set: (checked: boolean) => {
    if (!isUsingVModel()) {
      emit('update:checked', checked)
      emit('change', checked)
      emit('input', checked)
      return
    }

    // If modelValue is an array, add/remove this checkbox's value
    if (Array.isArray(modelValue.value)) {
      if (props.value === undefined || props.value === null) {
        emit('update:checked', checked)
        emit('change', checked)
        emit('input', checked)
        return
      }

      const newArray = [...modelValue.value]
      if (checked) {
        // Add value if not already present
        if (!newArray.includes(props.value)) {
          newArray.push(props.value)
        }
      } else {
        // Remove value if present
        const index = newArray.indexOf(props.value)
        if (index > -1) {
          newArray.splice(index, 1)
        }
      }
      modelValue.value = newArray as boolean | Array<string | number | boolean | null>
      emit('update:checked', checked)
      emit('change', checked)
      emit('input', checked)
    } else {
      // Boolean mode
      modelValue.value = checked as boolean | Array<string | number | boolean | null>
      emit('update:checked', checked)
      emit('change', checked)
      emit('input', checked)
    }
  },
})

// Set indeterminate state when prop changes
function setIndeterminate() {
  const checkbox = document.getElementById(inputId.value) as HTMLInputElement
  if (checkbox) {
    checkbox.indeterminate = props.indeterminate
  }
}

// Watch for indeterminate prop changes
watch(
  () => props.indeterminate,
  () => {
    nextTick(() => setIndeterminate())
  },
  { immediate: true },
)

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

<template>
  <div :class="[wrapperClasses, externalClass]">
    <label :for="inputId" :class="[innerWrapperClasses, { 'cursor-not-allowed': props.disabled }]" v-bind="labelAttrs">
      <input
        :value="props.value"
        v-model="internalChecked"
        :disabled="props.disabled"
        :required="props.required"
        type="checkbox"
        :class="inputClasses"
        v-bind="inputAttrs"
      />
      <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>
