<template>
  <div class="flex min-h-screen">
    <aside class="w-72 bg-m-bg p-6 text-white">
      <button
        type="button"
        class="text-xl text-black font-semibold"
        @click="clearSelection"
      >
        Liste des composants
      </button>

      <nav class="mt-6 flex flex-col gap-1">
        <div
          v-for="group in groups"
          :key="group.category"
        >
          <button
            type="button"
            class="flex w-full items-center justify-between rounded px-3 py-2 text-left text-black font-bold hover:bg-m-primary/10"
            @click="toggleCategory(group.category)"
          >
            {{ group.category }}
            <span
              class="text-xs transition-transform duration-200"
              :class="openCategories.has(group.category) ? 'rotate-90' : ''"
            >
              &#9654;
            </span>
          </button>

          <div
            v-if="openCategories.has(group.category)"
            class="ml-3 flex flex-col gap-1 border-l border-gray-300 pl-2"
          >
            <button
              v-for="item in group.items"
              :key="item.name"
              type="button"
              class="rounded px-3 py-1.5 text-left text-sm text-black hover:bg-m-primary hover:text-white"
              :class="selectedName === item.name ? 'bg-m-secondary text-white' : ''"
              @click="selectItem(item.name)"
            >
              {{ item.label }}
            </button>
          </div>
        </div>
      </nav>
    </aside>

    <main class="flex-1 p-6">
      <component
        :is="selectedDemoComponent"
        v-if="selectedDemoComponent"
      />
      <p
        v-else-if="selectedName"
        class="text-gray-700"
      >
        Page de demo introuvable:
        <code>.playground/pages/composant/{{ selectedDemoFileName }}.vue</code>
      </p>
      <div v-else>
        <h1 class="text-2xl font-semibold text-gray-900">
          Playground composants
        </h1>
        <p class="mt-2 text-gray-600">
          Selectionne un composant dans la liste pour afficher sa page de demo.
        </p>
      </div>
    </main>
  </div>
</template>

<script setup lang="ts">
import {computed, reactive, ref, watch, shallowRef} from 'vue'

type LoadedModule = {
  default: unknown
}

type Item = {
  name: string
  label: string
}

type Group = {
  category: string
  items: Item[]
}

const componentModules = import.meta.glob('../../app/components/malio/**/*.vue')
const demoModules = import.meta.glob('./composant/**/*.vue')

const demoByName: Record<string, () => Promise<LoadedModule>> =
  Object.fromEntries(
    Object.entries(demoModules).map(([file, loader]) => {
      const name = file.split('/').pop()?.replace('.vue', '') ?? ''
      return [name.toLowerCase(), loader as () => Promise<LoadedModule>]
    }),
  )

const groups = computed<Group[]>(() => {
  const categoryMap = new Map<string, Item[]>()

  Object.keys(componentModules).forEach((file) => {
    const parts = file.split('/')
    const name = parts.pop()?.replace('.vue', '') ?? ''
    const category = parts.pop() ?? ''

    if (!categoryMap.has(category)) {
      categoryMap.set(category, [])
    }
    categoryMap.get(category)!.push({name, label: name})
  })

  return Array.from(categoryMap.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([category, items]) => ({
      category: category.charAt(0).toUpperCase() + category.slice(1),
      items: items.sort((a, b) => a.label.localeCompare(b.label)),
    }))
})

const openCategories = reactive(new Set<string>())
const selectedName = ref('')
const hasInitializedSelection = ref(false)

watch(
  groups,
  (val) => {
    if (!hasInitializedSelection.value && val.length > 0) {
      openCategories.add(val[0].category)
      if (val[0].items.length > 0) {
        selectedName.value = val[0].items[0].name
      }
      hasInitializedSelection.value = true
    }
  },
  {immediate: true},
)

function toggleCategory(category: string) {
  if (openCategories.has(category)) {
    openCategories.delete(category)
  } else {
    openCategories.add(category)
  }
}

function selectItem(name: string) {
  selectedName.value = selectedName.value === name ? '' : name
}

function clearSelection() {
  selectedName.value = ''
}

const selectedDemoComponent = shallowRef<unknown>(null)

watch(selectedName, async (name) => {
  if (!name) {
    selectedDemoComponent.value = null
    return
  }

  const loader = demoByName[name.toLowerCase()]
  if (!loader) {
    selectedDemoComponent.value = null
    return
  }

  const mod = await loader()
  selectedDemoComponent.value = mod.default
})

const selectedDemoFileName = computed(() => {
  const name = selectedName.value
  if (!name) return ''
  return name.charAt(0).toLowerCase() + name.slice(1)
})
</script>
