<template>
  <section class="profile-section">
    <button
      type="button"
      class="section-header"
      :aria-expanded="isOpen"
      :aria-controls="contentId"
      @click="isOpen = !isOpen"
    >
      <span class="section-title">{{ title }}</span>
      <span class="section-chevron" :class="{ open: isOpen }" aria-hidden="true">›</span>
    </button>
    <div
      :id="contentId"
      ref="contentRef"
      class="section-body"
      :class="{ open: isOpen }"
      :style="bodyStyle"
      :inert="!isOpen"
      :aria-hidden="!isOpen"
    >
      <div ref="innerRef" class="section-inner">
        <slot />
      </div>
    </div>
  </section>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'

const props = defineProps<{
  title: string
  defaultOpen?: boolean
}>()

const isOpen = ref(props.defaultOpen ?? false)
const contentRef = ref<HTMLDivElement | null>(null)
const innerRef = ref<HTMLDivElement | null>(null)
const bodyHeight = ref<number | null>(null)

const contentId = computed(() => `section-${props.title.toLowerCase().replace(/\s+/g, '-')}`)

const bodyStyle = computed(() => {
  if (isOpen.value && bodyHeight.value !== null) {
    return {
      maxHeight: `${bodyHeight.value}px`,
      opacity: 1,
      visibility: 'visible',
    }
  }
  return {
    maxHeight: '0px',
    opacity: 0,
    visibility: 'hidden',
  }
})

async function measureHeight() {
  await nextTick()
  if (!contentRef.value || !innerRef.value) return
  // The outer body is currently collapsed (max-height:0 / visibility:hidden),
  // so scrollHeight would read 0. Briefly unclamp it off-screen to measure the
  // real content height, then restore the closed styles.
  const el = contentRef.value
  const original = {
    maxHeight: el.style.maxHeight,
    visibility: el.style.visibility,
    position: el.style.position,
  }
  el.style.maxHeight = 'none'
  el.style.visibility = 'hidden'
  el.style.position = 'absolute'
  const height = innerRef.value.scrollHeight
  el.style.maxHeight = original.maxHeight
  el.style.visibility = original.visibility
  el.style.position = original.position
  bodyHeight.value = height
}

watch(isOpen, (open) => {
  if (open) {
    measureHeight()
  }
})

let resizeObserver: ResizeObserver | null = null

onMounted(() => {
  if (isOpen.value) {
    measureHeight()
  }
  window.addEventListener('resize', measureHeight)

  if (innerRef.value && 'ResizeObserver' in window) {
    resizeObserver = new ResizeObserver(() => {
      if (isOpen.value) {
        measureHeight()
      }
    })
    resizeObserver.observe(innerRef.value)
  }
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', measureHeight)
  if (resizeObserver) {
    resizeObserver.disconnect()
  }
})
</script>

<style scoped>
.profile-section {
  border-bottom: 1px solid var(--form-input-border, #e6e6e6);
}

.profile-section:last-child {
  border-bottom: none;
}

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 1rem 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1rem;
  font-weight: 700;
  color: var(--form-heading, #667eea);
  text-align: left;
}

.section-header:focus {
  outline: none;
}

.section-header:focus-visible {
  outline: 2px solid var(--btn-primary, #667eea);
  outline-offset: 2px;
  border-radius: 4px;
}

.section-title {
  flex: 1;
}

.section-chevron {
  display: inline-block;
  font-size: 1.2rem;
  color: var(--text-secondary, #888);
  transition: transform 0.25s ease;
  margin-left: 0.5rem;
}

.section-chevron.open {
  transform: rotate(90deg);
}

.section-body {
  overflow: hidden;
  transition: max-height 0.3s ease, opacity 0.25s ease;
}

.section-inner {
  padding-bottom: 1.2rem;
}
</style>
