<template>
  <div class="settings-shell">
    <!-- Left rail: grouped, searchable nav that stays put while the right pane swaps -->
    <aside class="settings-rail">
      <h1 class="rail-title">Settings</h1>
      <div class="rail-search">
        <input v-model="search" type="text" class="search-input"
               placeholder="Search settings..." />
      </div>

      <nav class="rail-nav">
        <template v-for="group in visibleGroups" :key="group.title">
          <button
            type="button"
            class="rail-group-heading"
            :class="{ collapsed: !isExpanded(group.title) }"
            @click="toggleGroup(group.title)"
          >
            <span class="rail-chevron" aria-hidden="true"></span>
            {{ group.title }}
          </button>
          <template v-if="isExpanded(group.title)">
            <router-link
              v-for="card in group.cards"
              :key="card.to"
              :to="card.to"
              class="rail-link"
              :class="{ active: isActive(card.to) }"
            >
              <span class="rail-icon"><component :is="card.icon" :size="16" /></span>
              <span class="rail-label">{{ card.title }}</span>
            </router-link>
          </template>
        </template>
        <p v-if="!visibleGroups.length" class="rail-empty">No matching settings</p>
      </nav>
    </aside>

    <!-- Right pane: the selected settings page renders here -->
    <section class="settings-content">
      <router-view />
    </section>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useSettingsCatalog } from '../../composables/settingsCatalog'

const route = useRoute()

// Core settings groups merged with plugin-contributed cards (ADR-010).
const { groups } = useSettingsCatalog()

const search = ref('')

// Filter the rail by title/description; drop groups that end up empty.
const visibleGroups = computed(() => {
  const term = search.value.trim().toLowerCase()
  // Drop empty groups (e.g. the Measuring Tools placeholder when its plugin is
  // disabled) so a bare heading never renders.
  if (!term) return groups.value.filter(g => g.cards.length)
  return groups.value
    .map(g => ({
      title: g.title,
      cards: g.cards.filter(c =>
        c.title.toLowerCase().includes(term) ||
        c.description.toLowerCase().includes(term)),
    }))
    .filter(g => g.cards.length)
})

// A rail link is active when the current path matches its base path.
// Every settings section is now its own page, so a plain path compare suffices.
function isActive(to) {
  const base = to.split('?')[0]
  return route.path === base
}

// Collapsible groups so the 13-section rail fits without scrolling: only the
// group containing the current page is open; the user can toggle others.
// Searching forces every matching group open.
const groupOfActive = computed(() => {
  for (const g of groups.value) {
    if (g.cards.some(c => isActive(c.to))) return g.title
  }
  return groups.value[0]?.title
})
const manualToggles = ref({})
function toggleGroup(title) {
  manualToggles.value = { ...manualToggles.value, [title]: !isExpanded(title) }
}
function isExpanded(title) {
  if (search.value.trim()) return true
  if (title in manualToggles.value) return manualToggles.value[title]
  return title === groupOfActive.value
}
</script>

<style scoped>
.settings-shell {
  display: flex;
  align-items: flex-start;
  gap: 1.5rem;
}

.settings-rail {
  flex: 0 0 250px;
  position: sticky;
  top: 1rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}
.rail-title {
  margin: 0 0 0.5rem 0;
  font-size: 1.25rem;
}
.rail-search {
  margin-bottom: 0.5rem;
}
.search-input {
  width: 100%;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-card);
  color: var(--text);
  font-size: 0.85rem;
}

.rail-group-heading {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  width: 100%;
  margin: 0.35rem 0 0.15rem 0;
  padding: 0.2rem 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-light);
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
}
.rail-group-heading:hover {
  color: var(--text);
}
.rail-group-heading:first-child {
  margin-top: 0;
}
/* CSS-drawn caret (ASCII-only source): a right-pointing triangle that
   rotates down when the group is expanded. */
.rail-chevron {
  width: 0;
  height: 0;
  flex: 0 0 auto;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  border-left: 5px solid currentColor;
  transition: transform 0.12s ease;
}
.rail-group-heading:not(.collapsed) .rail-chevron {
  transform: rotate(90deg);
}

.rail-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.55rem;
  border-radius: 6px;
  text-decoration: none;
  color: var(--text);
  font-size: 0.86rem;
  border-left: 2px solid transparent;
}
.rail-link:hover {
  background: var(--bg);
}
.rail-link.active {
  background: var(--bg);
  border-left-color: var(--primary);
  color: var(--primary);
  font-weight: 600;
}
.rail-icon {
  display: inline-flex;
  color: var(--text-light);
}
.rail-link.active .rail-icon {
  color: var(--primary);
}
.rail-label {
  line-height: 1.2;
}
.rail-empty {
  color: var(--text-light);
  font-size: 0.85rem;
}

.settings-content {
  flex: 1 1 auto;
  min-width: 0;
}

@media (max-width: 820px) {
  .settings-shell {
    flex-direction: column;
  }
  .settings-rail {
    position: static;
    flex-basis: auto;
    width: 100%;
    max-height: none;
  }
}
</style>
