<template>
  <div v-bind="$attrs">
    <div v-if="isWindowed" class="flex items-end justify-center border-b border-m-primary">
      <button
        type="button"
        aria-label="Onglets précédents"
        data-test="tab-prev"
        :disabled="!canPrev"
        :class="[
          'mr-[36px] pb-[6px] text-m-primary',
          canPrev ? 'cursor-pointer hover:text-m-primary/70' : 'cursor-not-allowed text-m-primary/30',
        ]"
        @click="prev"
      >
        <IconifyIcon icon="mdi:chevron-left" :width="28" />
      </button>

      <div
        role="tablist"
        class="flex justify-center gap-[60px]"
      >
        <button
          v-for="tab in visibleTabs"
          :id="`${componentId}-tab-${tab.key}`"
          :key="tab.key"
          role="tab"
          type="button"
          :aria-selected="activeTab === tab.key"
          :aria-controls="`${componentId}-panel-${tab.key}`"
          :aria-disabled="!!tab.disabled"
          :tabindex="focusedKey === tab.key ? 0 : -1"
          :disabled="tab.disabled"
          :class="[
            'relative flex items-center gap-[18px] text-[24px] font-[600] transition-colors',
            activeTab === tab.key
              ? 'cursor-pointer text-m-primary after:content-[\'\'] after:absolute after:-bottom-[3px] after:left-0 after:right-0 after:h-[3px] after:bg-m-primary'
              : tab.disabled
                ? 'cursor-not-allowed text-m-primary/50'
                : 'cursor-pointer text-m-primary/50 hover:text-m-primary/70',
          ]"
          @click="selectTab(tab.key)"
        >
          <IconifyIcon
            v-if="tab.icon"
            :icon="tab.icon"
            :width="tab.iconSize ?? 24"
          />
          {{ tab.label }}
        </button>
      </div>

      <button
        type="button"
        aria-label="Onglets suivants"
        data-test="tab-next"
        :disabled="!canNext"
        :class="[
          'ml-[36px] pb-[6px] text-m-primary',
          canNext ? 'cursor-pointer hover:text-m-primary/70' : 'cursor-not-allowed text-m-primary/30',
        ]"
        @click="next"
      >
        <IconifyIcon icon="mdi:chevron-right" :width="28" />
      </button>
    </div>

    <div
      v-else
      role="tablist"
      class="flex justify-center gap-[60px] border-b border-m-primary"
    >
      <button
        v-for="tab in visibleTabs"
        :id="`${componentId}-tab-${tab.key}`"
        :key="tab.key"
        role="tab"
        type="button"
        :aria-selected="activeTab === tab.key"
        :aria-controls="`${componentId}-panel-${tab.key}`"
        :aria-disabled="!!tab.disabled"
        :tabindex="focusedKey === tab.key ? 0 : -1"
        :disabled="tab.disabled"
        :class="[
          'relative flex items-center gap-[18px] text-[24px] font-[600] transition-colors',
          activeTab === tab.key
            ? 'cursor-pointer text-m-primary after:content-[\'\'] after:absolute after:-bottom-[3px] after:left-0 after:right-0 after:h-[3px] after:bg-m-primary'
            : tab.disabled
              ? 'cursor-not-allowed text-m-primary/50'
              : 'cursor-pointer text-m-primary/50 hover:text-m-primary/70',
        ]"
        @click="selectTab(tab.key)"
      >
        <IconifyIcon
          v-if="tab.icon"
          :icon="tab.icon"
          :width="tab.iconSize ?? 24"
        />
        {{ tab.label }}
      </button>
    </div>

    <div
      v-for="tab in tabs"
      v-show="activeTab === tab.key"
      :id="`${componentId}-panel-${tab.key}`"
      :key="tab.key"
      role="tabpanel"
      :aria-labelledby="isTabRendered(tab.key) ? `${componentId}-tab-${tab.key}` : undefined"
      :aria-label="isTabRendered(tab.key) ? undefined : tab.label"
    >
      <slot :name="tab.key" />
    </div>
  </div>
</template>

<script setup lang="ts">
import {computed, ref, useId, watch} from 'vue'
import {Icon as IconifyIcon} from '@iconify/vue'

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

type Tab = {
  key: string
  label: string
  icon?: string
  iconSize?: string
  disabled?: boolean
}

const props = withDefaults(defineProps<{
  tabs: Tab[]
  modelValue?: string
  id?: string
  maxVisibleTabs?: number
}>(), {
  modelValue: undefined,
  id: '',
  maxVisibleTabs: undefined,
})

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

const generatedId = useId()
const componentId = computed(() => props.id || `malio-tab-list-${generatedId}`)

const isControlled = computed(() => props.modelValue !== undefined)
const localValue = ref(props.tabs.length > 0 ? props.tabs[0].key : '')

const activeTab = computed(() =>
  isControlled.value ? props.modelValue! : localValue.value,
)

const isWindowed = computed(() =>
  props.maxVisibleTabs != null && props.tabs.length > props.maxVisibleTabs,
)

const maxStartIndex = computed(() =>
  isWindowed.value ? Math.max(0, props.tabs.length - props.maxVisibleTabs!) : 0,
)

const startIndex = ref(0)

const visibleTabs = computed(() =>
  isWindowed.value
    ? props.tabs.slice(startIndex.value, startIndex.value + props.maxVisibleTabs!)
    : props.tabs,
)

const focusedKey = computed(() => {
  if (!isWindowed.value) return activeTab.value
  const inView = visibleTabs.value.some(t => t.key === activeTab.value)
  return inView ? activeTab.value : (visibleTabs.value[0]?.key ?? '')
})

const isTabRendered = (key: string) => !isWindowed.value || visibleTabs.value.some(t => t.key === key)

const canPrev = computed(() => isWindowed.value && startIndex.value > 0)
const canNext = computed(() => isWindowed.value && startIndex.value < maxStartIndex.value)

function prev() {
  if (!canPrev.value) return
  startIndex.value -= 1
}

function next() {
  if (!canNext.value) return
  startIndex.value += 1
}

// Clamp startIndex back in range if tabs or maxVisibleTabs change.
watch(maxStartIndex, (max) => {
  if (startIndex.value > max) startIndex.value = max
})

// Reset the window to the start when the tab list is replaced.
watch(() => props.tabs, () => {
  startIndex.value = 0
})

function selectTab(key: string) {
  const tab = props.tabs.find(t => t.key === key)
  if (tab?.disabled) return
  if (!isControlled.value) {
    localValue.value = key
  }
  emit('update:modelValue', key)
}
</script>
