<script setup lang="ts" generic="T = any">
import type { Swiper as SwiperType } from 'swiper'
import type { AutoplayOptions, CoverflowEffectOptions, PaginationOptions, SwiperOptions } from 'swiper/types'
import { Icon } from '@bagelink/vue'
import { Autoplay, EffectCoverflow, EffectCube, EffectFade, EffectFlip, Navigation, Pagination, EffectCards } from 'swiper/modules'
import { Swiper, SwiperSlide } from 'swiper/vue'
import { computed, ref, watch } from 'vue'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import 'swiper/css/effect-fade'
import 'swiper/css/effect-coverflow'
import 'swiper/css/effect-cube'
import 'swiper/css/effect-flip'
import 'swiper/css/effect-cards'

defineOptions({ name: 'BglSwiper' })

type SwiperEffect = 'slide' | 'fade' | 'cube' | 'coverflow' | 'flip' | 'cards'
type SwiperDirection = 'horizontal' | 'vertical'
type SwiperVariant = 'default' | 'testimonial' | 'gallery' | 'cards' | 'coverflow' | 'hero'

interface VariantConfig {
	slidesPerView?: number | 'auto'
	spaceBetween?: number
	effect?: SwiperEffect
	loop?: boolean
	autoplay?: boolean | AutoplayOptions
	pagination?: boolean | PaginationOptions
	navigation?: boolean
	speed?: number
	centeredSlides?: boolean
	coverflowEffect?: Partial<CoverflowEffectOptions>
	breakpoints?: SwiperOptions['breakpoints']
}

const props = withDefaults(
	defineProps<{
		// Items to render
		items?: T[]

		// Active index (v-model support)
		index?: number

		// Preset variant
		variant?: SwiperVariant

		// Slide dimensions
		slideWidth?: number | string

		// Basic swiper options
		effect?: SwiperEffect
		direction?: SwiperDirection
		speed?: number
		loop?: boolean
		initialSlide?: number

		// Slides display
		slidesPerView?: number | 'auto'
		spaceBetween?: number
		centeredSlides?: boolean

		// Interaction
		grabCursor?: boolean
		keyboard?: boolean
		mousewheel?: boolean

		// Auto height
		autoHeight?: boolean

		// Navigation
		navigation?: boolean

		// Pagination
		pagination?: boolean | PaginationOptions

		// Autoplay
		autoplay?: boolean | AutoplayOptions

		// Effect-specific options
		coverflowEffect?: Partial<CoverflowEffectOptions>

		// Responsive breakpoints
		breakpoints?: SwiperOptions['breakpoints']

		// Advanced: allow override with raw options
		advancedOptions?: SwiperOptions
	}>(),
	{
		items: undefined,
		index: undefined,
		variant: 'default',
		slideWidth: undefined,
		effect: undefined,
		direction: 'horizontal',
		speed: undefined,
		loop: undefined,
		initialSlide: 0,
		slidesPerView: undefined,
		spaceBetween: undefined,
		centeredSlides: undefined,
		grabCursor: true,
		keyboard: true,
		mousewheel: false,
		autoHeight: false,
		navigation: undefined,
		pagination: undefined,
		autoplay: undefined,
		coverflowEffect: undefined,
		breakpoints: undefined,
		advancedOptions: undefined,
	},
)

const emit = defineEmits<{
	'update:index': [index: number]
}>()

defineSlots<{
	'default'?: (props: { item: T, index: number, currentIndex: number }) => any
	'prev-button'?: (props: { prev: () => void }) => any
	'next-button'?: (props: { next: () => void }) => any
	'navigation'?: (props: {
		items: T[]
		currentIndex: number
		slideTo: (index: number) => void
		prev: () => void
		next: () => void
		isFirst: boolean
		isLast: boolean
	}) => any
	'pagination'?: () => any
}>()

const variantPresets: Record<SwiperVariant, VariantConfig> = {
	default: {},
	testimonial: {
		slidesPerView: 1,
		autoplay: { delay: 5000, disableOnInteraction: false },
		pagination: { clickable: true },
		loop: true,
		speed: 600,
	},
	gallery: {
		slidesPerView: 1,
		spaceBetween: 0,
		navigation: true,
		loop: true,
	},
	cards: {
		slidesPerView: 3,
		spaceBetween: 20,
		navigation: true,
		breakpoints: {
			320: { slidesPerView: 1, spaceBetween: 10 },
			768: { slidesPerView: 2, spaceBetween: 15 },
			1024: { slidesPerView: 3, spaceBetween: 20 },
		},
	},
	coverflow: {
		effect: 'coverflow',
		slidesPerView: 'auto',
		centeredSlides: true,
		loop: true,
		navigation: true,
		coverflowEffect: {
			rotate: -2,
			stretch: 0,
			depth: 100,
			slideShadows: true,
			modifier: 1,
			scale: 0.98,
		},
	},
	hero: {
		slidesPerView: 1,
		autoplay: { delay: 4000 },
		pagination: { clickable: true },
		loop: true,
		speed: 800,
		effect: 'fade',
	},
}

const swiperEl = ref<SwiperType | null>(null)
const isFirstSlide = ref(true)
const isLastSlide = ref(false)
const currentIndex = ref(props.index ?? props.initialSlide ?? 0)
let isUpdatingFromExternal = false // Guard to prevent update loops

const slideStyles = computed(() => {
	const styles: Record<string, string> = {}

	if (props.slideWidth !== undefined) {
		styles.width = typeof props.slideWidth === 'number' ? `${props.slideWidth}px` : props.slideWidth
	}

	return styles
})

// Apply variant preset configuration
const variantConfig = computed(() => variantPresets[props.variant])

// Smart default: if slideWidth is set and slidesPerView wasn't set,
// automatically use 'auto' to let slides determine their own width
const effectiveSlidesPerView = computed(() => {
	// Priority: explicit prop > variant config > smart default
	if (props.slidesPerView !== undefined) {
		return props.slidesPerView
	}

	if (variantConfig.value.slidesPerView !== undefined) {
		return variantConfig.value.slidesPerView
	}

	// Smart default: if slideWidth is provided, use 'auto'
	if (props.slideWidth !== undefined) {
		return 'auto'
	}

	// Final fallback
	return 1
})

const swiperParams = computed((): SwiperOptions => {
	// Helper to get value with priority: prop > variant > default
	const getValue = <K extends keyof VariantConfig>(
		propValue: any,
		variantKey: K,
		defaultValue: any
	) => {
		if (propValue !== undefined) return propValue
		if (variantConfig.value[variantKey] !== undefined) return variantConfig.value[variantKey]
		return defaultValue
	}

	const params: SwiperOptions = {
		modules: [Navigation, Pagination, Autoplay, EffectFade, EffectCoverflow, EffectCube, EffectFlip, EffectCards],
		effect: getValue(props.effect, 'effect', 'slide'),
		direction: props.direction ?? 'horizontal',
		speed: getValue(props.speed, 'speed', 300),
		loop: getValue(props.loop, 'loop', false),
		initialSlide: props.index ?? props.initialSlide,
		slidesPerView: effectiveSlidesPerView.value,
		spaceBetween: getValue(props.spaceBetween, 'spaceBetween', 0),
		centeredSlides: getValue(props.centeredSlides, 'centeredSlides', false),
		grabCursor: props.grabCursor,
		keyboard: props.keyboard,
		mousewheel: props.mousewheel,
		autoHeight: props.autoHeight,
	}

	// Navigation (priority: prop > variant > default)
	// const navigationEnabled = getValue(props.navigation, 'navigation', false)
	// // Note: We use custom navigation controls, not Swiper's built-in navigation
	// // params.navigation = !!navigationEnabled

	// Pagination (priority: prop > variant > default)
	const paginationConfig = getValue(props.pagination, 'pagination', false)
	params.pagination = paginationConfig
		? (typeof paginationConfig === 'boolean' ? { clickable: true } : paginationConfig)
		: false

	// Autoplay (priority: prop > variant > default)
	const autoplayConfig = getValue(props.autoplay, 'autoplay', false)
	params.autoplay = autoplayConfig
		? (typeof autoplayConfig === 'boolean' ? { delay: 3000 } : autoplayConfig)
		: false

	// Effect-specific options (merge variant and explicit)
	const effectType = params.effect || 'slide'
	if (effectType === 'coverflow') {
		const baseCoverflowEffect = {
			rotate: 0,
			stretch: 0,
			depth: 100,
			modifier: 1,
			slideShadows: true,
		}
		params.coverflowEffect = {
			...baseCoverflowEffect,
			...variantConfig.value.coverflowEffect,
			...props.coverflowEffect,
		}
	}

	// Breakpoints (priority: prop > variant)
	const breakpointsConfig = getValue(props.breakpoints, 'breakpoints', undefined)
	if (breakpointsConfig) {
		params.breakpoints = breakpointsConfig
	}

	// Advanced options override
	if (props.advancedOptions) {
		return { ...params, ...props.advancedOptions }
	}

	return params
})

// Format params for Swiper Vue component with proper types
const swiperVueProps = computed(() => ({
	modules: swiperParams.value.modules,
	effect: swiperParams.value.effect as 'slide' | 'fade' | 'cube' | 'coverflow' | 'flip' | 'cards',
	direction: swiperParams.value.direction,
	speed: swiperParams.value.speed,
	loop: swiperParams.value.loop,
	initialSlide: swiperParams.value.initialSlide,
	slidesPerView: swiperParams.value.slidesPerView,
	spaceBetween: swiperParams.value.spaceBetween as number,
	centeredSlides: swiperParams.value.centeredSlides,
	grabCursor: swiperParams.value.grabCursor,
	keyboard: swiperParams.value.keyboard as boolean | undefined,
	mousewheel: swiperParams.value.mousewheel as boolean | undefined,
	autoHeight: swiperParams.value.autoHeight,
	navigation: swiperParams.value.navigation as boolean | undefined,
	pagination: swiperParams.value.pagination,
	autoplay: swiperParams.value.autoplay,
	coverflowEffect: swiperParams.value.coverflowEffect,
	breakpoints: swiperParams.value.breakpoints,
}))

const swiperInstance = computed(() => swiperEl.value)

function updateNavigationState() {
	if (swiperEl.value && !isUpdatingFromExternal) {
		isFirstSlide.value = swiperEl.value.isBeginning
		isLastSlide.value = swiperEl.value.isEnd

		// Emit index update for v-model support
		// Use activeIndex for non-loop mode, realIndex for loop mode
		const newIndex = props.loop ? swiperEl.value.realIndex : swiperEl.value.activeIndex
		currentIndex.value = newIndex

		// Emit to keep external state in sync
		emit('update:index', newIndex)
	}
}

function slideTo(index: number) {
	if (!swiperEl.value) return
	swiperEl.value.slideTo(index)
}

function handleSlideNav(dir: 'prev' | 'next') {
	if (!swiperEl.value) return

	if (dir === 'next') {
		swiperEl.value.slideNext()
	} else {
		swiperEl.value.slidePrev()
	}
}

function onSwiper(swiper: SwiperType) {
	swiperEl.value = swiper
	updateNavigationState()
}

watch(
	() => props.initialSlide,
	(index) => {
		if (index !== undefined && swiperEl.value) {
			setTimeout(() => {
				if (swiperEl.value) swiperEl.value.slideTo(index)
			}, 1)
		}
	},
	{ immediate: true },
)

// Watch for external index changes (v-model support)
watch(
	() => props.index,
	(newIndex) => {
		if (newIndex !== undefined) {
			currentIndex.value = newIndex

			if (swiperEl.value) {
				const activeIndex = props.loop ? swiperEl.value.realIndex : swiperEl.value.activeIndex
				if (newIndex !== activeIndex) {
					isUpdatingFromExternal = true
					swiperEl.value.slideTo(newIndex)
					// Reset guard after a short delay to allow transition
					setTimeout(() => {
						isUpdatingFromExternal = false
					}, 50)
				}
			}
		}
	},
)

// Watch for param changes and update swiper
watch(
	swiperParams,
	(newParams) => {
		if (swiperEl.value) {
			Object.assign(swiperEl.value.params, newParams)
			swiperEl.value.update()
		}
	},
	{ deep: true },
)

defineExpose({
	swiper: swiperInstance,
	slideNext: () => { handleSlideNav('next') },
	slidePrev: () => { handleSlideNav('prev') },
	slideTo,
	update: () => swiperEl.value?.update(),
	isFirst: isFirstSlide,
	isLast: isLastSlide,
	currentIndex,
})
</script>

<template>
	<div class="swi-wrap">
		<Swiper
			v-bind="swiperVueProps" class="swiper" @swiper="onSwiper" @slide-change="updateNavigationState"
			@reach-beginning="updateNavigationState" @reach-end="updateNavigationState"
			@from-edge="updateNavigationState"
		>
			<SwiperSlide v-for="(item, idx) in items" :key="idx" :style="slideStyles">
				<slot :item="item" :index="idx" :currentIndex="currentIndex" />
			</SwiperSlide>
		</Swiper>

		<!-- Custom Navigation Slot -->
		<slot
			name="navigation" :items="items || []" :currentIndex="currentIndex" :slide-to="slideTo"
			:prev="() => handleSlideNav('prev')" :next="() => handleSlideNav('next')" :is-first="isFirstSlide"
			:is-last="isLastSlide"
		>
			<!-- Default Navigation -->
			<div v-if="navigation" class="swi-ctrl">
				<div class="swi-prev hover" @click="handleSlideNav('prev')">
					<slot name="prev-button" :prev="() => handleSlideNav('prev')">
						<Icon name="chevron_left" class="user-select-none hover z-99   color-primary" :size="2" />
					</slot>
				</div>
				<div class="swi-next hover" @click="handleSlideNav('next')">
					<slot name="next-button" :next="() => handleSlideNav('next')">
						<Icon name="chevron_right" class="user-select-none hover z-99   color-primary" :size="2" />
					</slot>
				</div>
			</div>
		</slot>

		<!-- Custom Pagination Slot -->
		<slot v-if="pagination" name="pagination" />
	</div>
</template>

<style>

.swiper {
width: 100%;
}

:root {
--swiper-navigation-color: white;
--swiper-pagination-color: white;
}

/* Pagination: keep Swiper's own horizontal centering (left:50% + translateX),
   just give the wrapper a sane bottom offset and consistent bullet sizing so
   consumers don't need :deep() overrides to fix position. Override the bullet
   look with --swiper-pagination-* vars. */
.swi-wrap .swiper-pagination-bullets {
bottom: 8px;
}
.swi-wrap .swiper-pagination-bullet {
width: var(--swiper-pagination-bullet-width, 7px);
height: var(--swiper-pagination-bullet-height, 7px);
background: var(--swiper-pagination-bullet-inactive-color, #fff);
opacity: var(--swiper-pagination-bullet-inactive-opacity, 0.6);
}
.swi-wrap .swiper-pagination-bullet-active {
opacity: 1;
background: var(--swiper-pagination-color, #fff);
}

.swi-ctrl {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
z-index: 99;
display: flex;
justify-content: space-between;
padding: 0rem 4rem;
height: 0;
pointer-events: none;
}

.swi-ctrl>* {
pointer-events: auto;
}

.swi-ctrl img {
height: 20px;
}

.swi-wrap {
isolation: isolate;
position: relative;
}

.swi-prev,
.swi-next {
background: var(--bgl-primary);
height: 40px;
width: 40px;
border-radius: 100%;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: opacity 0.3s ease;
}

.swi-prev:hover,
.swi-next:hover {
opacity: 0.8;
}

@media screen and (max-width: 910px) {
.swi-ctrl {
	padding: 0rem 0.5rem;
}
}

</style>
