<script setup lang="ts">
import { Skeleton, normalizeDimension, appendScript, normalizeURL, Icon, pathKeyToURL } from '@bagelink/vue'
import { ref, watch } from 'vue'

interface ImageProps {
	src?: string
	pathKey?: string
	alt?: string
	width?: string | number
	height?: string | number
	caption?: string
	modelValue?: string
}

declare global {
	interface Window {
		heic2any: any
	}
}

const props = defineProps<ImageProps>()

const imageSrc = ref<string | undefined>(undefined)
const loadingError = ref(false)

function getImageUrl(): string | undefined {
	if ((props.src === undefined || props.src === '') && (props.pathKey === undefined || props.pathKey === '') && (props.modelValue === undefined || props.modelValue === '')) { return }
	return pathKeyToURL(props.src ?? props.pathKey ?? props.modelValue)
}

async function getCachedImage(url: string): Promise<string | undefined> {
	if (!('caches' in window)) { return undefined }
	try {
		const imgCache = await window.caches.open('img-cache')
		const cachedResponse = await imgCache.match(url)
		if (cachedResponse) {
			return URL.createObjectURL(await cachedResponse.blob())
		}
	} catch (error) {
		console.warn('Cache access error:', error)
	}
	return undefined
}

async function cacheImage(url: string, blob: Blob) {
	if (!('caches' in window)) { return }
	try {
		const imgCache = await window.caches.open('img-cache')
		await imgCache.put(url, new Response(blob))
	} catch (error) {
		console.warn('Cache write error:', error)
	}
}

async function convertHeicImage(url: string): Promise<string> {
	await appendScript('https://cdnjs.cloudflare.com/ajax/libs/heic2any/0.0.1/index.min.js')
	const response = await fetch(normalizeURL(url))
	const blob = await response.blob()
	const convertedBlob = await window.heic2any({ blob }) as Blob
	await cacheImage(url, convertedBlob)
	return URL.createObjectURL(convertedBlob)
}

async function loadImage() {
	loadingError.value = false
	const url = getImageUrl()
	if (url === undefined || url === '') {
		imageSrc.value = undefined
		return
	}

	try {
		const ext = url.split('.').pop()?.toLowerCase().split('?')[0]

		if (ext === 'heic') {
			const cachedSrc = await getCachedImage(url)
			if (cachedSrc !== undefined && cachedSrc !== '') {
				imageSrc.value = cachedSrc
				return
			}
			imageSrc.value = await convertHeicImage(url)
		} else {
			imageSrc.value = url
		}
	} catch (error) {
		console.error('Image loading error:', error)
		loadingError.value = true
		imageSrc.value = undefined
	}
}

watch(() => [props.src, props.pathKey, props.modelValue], loadImage, { immediate: true })
</script>

<template>
	<figcaption v-if="caption">
		<img
			v-if="imageSrc"
			:src="imageSrc"
			v-bind="$attrs"
			:alt="alt"
			:width="normalizeDimension(width)"
			:height="normalizeDimension(height)"
		>
		<Skeleton
			v-else
			class="img-web-kit"
			:width="normalizeDimension(width)"
			:height="normalizeDimension(height)"
		/>
	</figcaption>

	<img
		v-else-if="imageSrc"
		:src="imageSrc"
		v-bind="$attrs"
		:alt="alt"
		:width="normalizeDimension(width)"
		:height="normalizeDimension(height)"
	>
	<div
		v-else-if="loadingError"
		class="flex-center error-image"
		:style="{
			width: normalizeDimension(width),
			height: normalizeDimension(height),
		}"
	>
		<Icon name="broken_image" />
	</div>
	<Skeleton
		v-else
		class="img-web-kit"
		:width="normalizeDimension(width)"
		:height="normalizeDimension(height)"
	/>
</template>

<style scoped>
.img-web-kit {
	max-width: 100%;
	vertical-align: middle;
	border: 0;
	width: 100%;
}
.error-image {
	background-color: var(--skeleton-bg);
}
</style>
