<script lang="ts" setup>
import { Icon } from '@bagelink/vue'
import { computed, inject, ref, watch } from 'vue'

const props = defineProps<{
	label?: string
	id?: string
	open?: boolean
	iconType?: 'expand_more' | 'plus_minus'
	iconPosition?: 'start' | 'end'
	flat?: boolean
	iconClass?: string
}>()

const emit = defineEmits(['update:open'])

const openVal = ref(!!props.open)

const isOpen = computed({
	get: () => openVal.value,
	set: (value: boolean) => {
		openVal.value = value
		emit('update:open', value)
	},
})

interface AccordionState {
	openItem?: string
}

const accordionState = inject<AccordionState | undefined>('accordionState', undefined)
const id = props.id || Math.random().toString(36).slice(7)

const computedIcon = computed(() => {
	return props.iconType === 'plus_minus'
		? (isOpen.value ? 'remove' : 'add')
		: 'expand_more'
})
const iconPosition = computed(() => props.iconPosition || 'end')

watch(() => props.open, () => isOpen.value = props.open, { immediate: true })

if (accordionState) {
	watch(
		() => accordionState.openItem,
		(currentOpenId) => {
			if (currentOpenId !== id) { isOpen.value = false }
		},
		{ immediate: true }
	)
}

function toggle() {
	isOpen.value = !isOpen.value
	if (accordionState) {
		if (isOpen.value) {
			accordionState.openItem = id
		} else if (accordionState.openItem !== undefined && accordionState.openItem === id) {
			accordionState.openItem = undefined
		}
	}
}
</script>

<template>
	<div class="accordion-item txt-start" :class="[{ flat }]">
		<button
			:aria-expanded="isOpen ? 'true' : 'false'" class="accordion-head"
			:aria-controls="`accordion-body-${id}`" @click="toggle()"
		>
			<span
				v-if="iconPosition === 'start'" class="accordion-icon"
				:class="[iconClass, { open: isOpen && iconType === 'expand_more' }]"
			>
				<Icon :icon="computedIcon" transition="scale" />
			</span>

			<slot name="head">
				<span class="accordion-label">
					{{ label }}
				</span>
			</slot>

			<span
				v-if="iconPosition === 'end'" class="accordion-icon"
				:class="[iconClass, { open: isOpen && iconType === 'expand_more' }]"
			>
				<Icon :icon="computedIcon" transition="scale" />
			</span>
		</button>
		<div
			:id="`accordion-body-${id}`" class="accordion-body"
			:class="{ open: isOpen }" :aria-hidden="isOpen ? 'false' : 'true'"
		>
			<div class="accordion-body-inner">
				<slot />
			</div>
		</div>
	</div>
</template>

<style scoped>

.accordion-item {
border-bottom: 1px solid var(--bgl-border-color);
transition: all 0.2s;
overflow: hidden;
}

.accordion-item button {
cursor: pointer;
}

.accordion-head {
height: var(--bgl-input-height);
background: transparent;
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
border: none;
color: inherit !important;
}

.accordion-icon {
transition: all 0.2s ease;
display: flex;
align-items: center;
flex-shrink: 0;
width: calc(var(--bgl-btn-height) / 1.5);
height: calc(var(--bgl-btn-height) / 1.5);
padding-inline: calc(var(--bgl-btn-padding) / 3);
justify-content: center;
}

[aria-expanded="true"] .accordion-icon {
transform: rotate(180deg);
}

.accordion-label {
font-weight: bold;
}

.flat.accordion-item {
border-bottom: none
}

.accordion-head:hover .accordion-label {
text-decoration: underline;
}

/* Height animation via grid-template-rows 0fr -> 1fr. Unlike a fixed max-height,
   this animates to the content's natural height with no cap (no clipping on long
   bodies) and no JS measurement. The inner wrapper needs min-height:0 + overflow
   hidden so it can collapse fully. */
.accordion-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.25s ease;
}

.accordion-body.open {
grid-template-rows: 1fr;
}

.accordion-body-inner {
min-height: 0;
overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
.accordion-body { transition: none; }
}
</style>
