<script lang="ts" setup>
import type { IconType, ThemeType } from '@bagelink/vue'
import { Avatar, Icon } from '@bagelink/vue'
import { computed, useAttrs } from 'vue'

defineOptions({ inheritAttrs: false })

const props = withDefaults(
	defineProps<{
		src?: string
		showAvatar?: boolean
		to?: string
		href?: string
		icon?: IconType
		title?: string
		subtitle?: string
		flat?: boolean
		disabled?: boolean
		lead?: string
		end?: string
		iconEnd?: IconType
		target?: '_blank' | '_self'
		thin?: boolean
		fullWidth?: boolean
		ellipsis?: boolean
		ripple?: boolean
		/** Theme accent for the leading icon-chip and hover/active tint.
		    Defaults to the surface's primary. e.g. `color="purple"`. */
		color?: ThemeType
		/** Card-style row: rounded, no divider, tinted hover. Use for standalone
		    selectable lists (vs. the default flush divided rows in a panel). */
		rounded?: boolean
		/** Visually mark as selected (for non-router selection lists). */
		active?: boolean
		/** Render as an interactive row (cursor + hover) without needing a handler.
		    Implied when `to`, `href`, or a `@click` listener is set. */
		clickable?: boolean
	}>(),
	{
		ellipsis: true,
		ripple: false,
	}
)

const attrs = useAttrs()

// Split inherited attrs: event listeners (onClick, onClickStop, …) belong on the
// clickable element so handlers + modifiers like `.stop` fire on the actual
// button/link row; everything else (class, style, data-*, …) stays on the root
// wrapper to preserve existing behavior.
const listenerAttrs = computed(() => {
	const out: Record<string, any> = {}
	for (const k of Object.keys(attrs)) {
		if (/^on[A-Z]/.test(k)) { out[k] = (attrs as any)[k] }
	}
	return out
})
const rootAttrs = computed(() => {
	const out: Record<string, any> = {}
	for (const k of Object.keys(attrs)) {
		if (!/^on[A-Z]/.test(k)) { out[k] = (attrs as any)[k] }
	}
	return out
})

const hasTo = computed(() => props.to !== undefined && props.to !== '')
const hasHref = computed(() => props.href !== undefined && props.href !== '')
// A `@click` listener (with or without modifiers like `.stop`) shows up in
// $attrs as `onClick` / `onClickStop` / etc — detect any of them so a clickable
// row is recognized even when the handler uses modifiers.
const hasClickListener = computed(() => Object.keys(attrs).some(k => /^onClick/i.test(k)))
const isClickable = computed(() => hasTo.value || hasHref.value || hasClickListener.value || props.clickable === true)

const isComponent = computed(() => {
	if (hasTo.value) { return 'router-link' }
	if (hasHref.value) { return 'a' }
	if (isClickable.value) { return 'button' }
	return 'div'
})

const bind = computed(() => {
	// Spread the inherited event listeners (including `@click`/`@click.stop`) onto
	// the clickable element itself, so the handler fires on the actual button/link
	// row — not the outer wrapper — and modifiers like `.stop` keep working.
	const obj: { [key: string]: any } = { ...listenerAttrs.value }
	if (props.to !== undefined && props.to !== '') { obj.to = props.to }
	else if (props.href !== undefined && props.href !== '') { obj.href = props.href }
	if (props.target !== undefined && props.target !== undefined && ((props.to !== undefined && props.to !== '') || (props.href !== undefined && props.href !== ''))) { obj.target = props.target }

	obj.class = {
		'notClickable': !isClickable.value,
		'no-border-list': props.flat,
	}
	if (props.disabled) { obj.disabled = true }
	return obj
})
</script>

<template>
	<div
		v-bind="rootAttrs"
		class="flex space-between list-item-row bg"
		:class="{ 'no-border-list': props.flat || rounded, 'list-item-flush': props.fullWidth, 'list-item-fullrow': isClickable, 'list-item-rounded': rounded, 'list-item-active': active }"
		:style="color ? { '--bgl-list-item-accent': `var(--bgl-${color})` } : undefined"
	>
		<!-- Content rendered before the clickable area (e.g. a drag handle, avatar
		     or leading visual). Lives outside the clickable component so interacting
		     with it never triggers navigation/selection. -->
		<div v-if="$slots.start" class="list-item-start flex align-items-center flex-shrink-0">
			<slot name="start" />
		</div>
		<component
			:is="isComponent" v-bind="bind" v-ripple="ripple && isClickable"
			class="flex flex-grow-1 gap-075 align-items-center list-item" :class="{
				'py-075': !props.thin,
				'py-05': props.thin,
				'px-1': !props.fullWidth,
				'px-0': props.fullWidth,
			}"
		>
			<!-- Leading visual INSIDE the clickable area. Use #media for custom art
			     (covers, thumbnails); `src`/`showAvatar` and `icon` are shortcuts. -->
			<slot name="media">
				<Avatar v-if="src || showAvatar" :name="title" :src="src" :size="thin ? 30 : 38" class="flex-shrink-0" />
				<span v-if="icon" class="list-item-icon flex-shrink-0">
					<Icon size="1.2" :icon="icon" />
				</span>
			</slot>

			<div class="txt-start flex-grow-1 list-item-body">
				<p v-if="lead" class="list-item-lead" :class="{ ellipsis }">
					{{ lead }}
					<slot name="lead" />
				</p>
				<p class="list-item-title" :class="{ 'ellipsis-1': ellipsis }">
					{{ title }}
					<slot />
				</p>
				<p v-if="subtitle || $slots.subtitle" class="list-item-subtitle" :class="{ 'ellipsis-1': ellipsis }">
					{{ subtitle }}
					<slot name="subtitle" />
				</p>
			</div>
		</component>
		<!-- Trailing meta. For clickable rows it floats over the end of the click
		     button so the button spans the whole row; the end content stays on top
		     and independently clickable. Tune room with --bgl-list-item-end-space. -->
		<div class="list-item-end flex align-items-center gap-05 flex-shrink-0">
			<slot name="end">
				<span v-if="end" class="list-item-endtext" :class="{ 'ellipsis-1': ellipsis }" v-text="end" />
				<Icon v-if="iconEnd" :icon="iconEnd" class="transition-400 opacity-4" />
			</slot>
		</div>
	</div>
</template>

<style>

/* The row wraps the clickable area + start/end slots and owns the divider.
   Center start/end slots against the (possibly taller) text block by default —
   the common case for lists with a leading icon/handle and trailing meta. */
.list-item-row {
position: relative;
align-items: center;
gap: 0.5rem;
}

/* Full-row click target: the clickable button stretches to the row's trailing
   edge so hover/active read across the whole item. The trailing meta is lifted
   out of flow and floated over the button's end — staying visible and
   independently clickable. The button reserves matching room so text never sits
   under the meta. Tune the reserved width with --bgl-list-item-end-space. */
.list-item-fullrow {
--bgl-list-item-end-space: 3.5rem;
/* Keep the trailing meta aligned with the row's own inline padding (1rem) so it
   doesn't crowd the edge. Override per-list with --bgl-list-item-end-inset. */
--bgl-list-item-end-inset: 1rem;
}
.list-item-fullrow .list-item-end {
/* position: absolute; */
inset-inline-end: var(--bgl-list-item-end-inset);
/* top: 50%; */
/* transform: translateY(-50%); */
z-index: 1;
/* non-interactive meta lets clicks fall through to the row button… */
pointer-events: none;
padding-inline-end: 1rem;
}
/* …interactive trailing controls stay clickable on top of the row button. */
.list-item-fullrow .list-item-end :where(button, a, input, select, label, [role="button"], [tabindex]) {
pointer-events: auto;
}
.list-item-fullrow .list-item:not(.px-0) { padding-inline-end: var(--bgl-list-item-end-space); }
.list-item-fullrow.list-item-flush .list-item-end { --bgl-list-item-end-inset: 0; }
.list-item-row::after {
content: '';
position: absolute;
inset-inline: 0;
bottom: 0;
border-bottom: 1px solid var(--bgl-border-color);
}
.list-item-row:last-child::after,
.list-item-row.no-border-list::after {
border-bottom: none;
}

button.list-item {
border: none;
background-color: unset;
width: 100%;
font: inherit;
text-align: inherit;
}

.list-item {
min-height: fit-content;
cursor: pointer;
transition: background-color 0.15s ease;
/* Inherit color so the component adapts to ANY surface (light, dark, a
   hard-coded black panel) without per-consumer overrides. On a normal
   Bagelink surface `currentColor` resolves to --bgl-text-color. */
color: inherit;
text-decoration: none;
border-radius: var(--bgl-btn-border-radius);
}

.list-item:disabled {
opacity: 0.5;
pointer-events: none;
}

/* Accent used by the icon-chip and (for rounded/active) the tint. Defaults to
   primary; set per-item via `color` prop → --bgl-list-item-accent. */
.list-item-row {
--bgl-list-item-accent: var(--bgl-primary);
}

/* Hover/active use Bagelink's translucent gray tint, which already flips from
   translucent-dark (light mode) to translucent-light (.bgl-dark-mode) — so it
   reads correctly on any background. Override per-list with --bgl-list-item-hover. */
.list-item:hover,
.list-item.router-link-exact-active {
background-color: var(--bgl-list-item-hover, var(--bgl-gray-tint));
}
.list-item.router-link-exact-active,
.list-item-active .list-item {
background-color: var(--bgl-list-item-active, color-mix(in srgb, var(--bgl-list-item-accent) 12%, transparent));
color: var(--bgl-list-item-accent);
}

/* Rounded card-style rows: pill-radius, tinted accent hover, no divider. */
.list-item-rounded .list-item {
border-radius: var(--bgl-card-border-radius);
}
.list-item-rounded .list-item:hover {
background-color: var(--bgl-list-item-hover, color-mix(in srgb, var(--bgl-list-item-accent) 8%, transparent));
}

.notClickable,
.notClickable:hover {
background-color: unset;
cursor: default;
}

.list-item:active {
filter: var(--bgl-hover-filter);
}

/* Leading icon chip — tinted with the row accent (primary by default). */
.list-item-icon {
display: grid;
place-items: center;
width: 34px;
height: 34px;
border-radius: 9px;
background: color-mix(in srgb, var(--bgl-list-item-accent) 12%, transparent);
color: var(--bgl-list-item-accent);
}

/* Text block: tight, clear hierarchy */
.list-item-body > p { margin: 0; padding: 0; }
.list-item-lead {
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
opacity: 0.5;
line-height: 1.3;
}
.list-item-title {
font-size: 0.9375rem;
font-weight: 600;
line-height: 1.35;
}
.list-item-subtitle {
font-size: 0.8125rem;
opacity: 0.6;
line-height: 1.35;
font-weight: 300;
margin-top: 1px;
}
.list-item-endtext {
font-size: 0.8125rem;
opacity: 0.6;
white-space: nowrap;
}
.list-item-end:empty { display: none; }

</style>
