<script setup lang="ts">
import type { IconType } from '@bagelink/vue'
import { useDevice } from '@bagelink/vue'
import { computed, ref, onMounted, watchEffect } from 'vue'
import { FONT_AWESOME_ICONS, MATERIAL_ICONS, FONT_AWESOME_BRANDS_ICONS } from './constants'

const props = withDefaults(defineProps<{
	icon?: IconType
	name?: IconType
	size?: number | string
	mobileSize?: number | string
	color?: string
	round?: boolean
	weight?: number | string
	fontAwesome?: boolean
	fill?: boolean
}>(), {
	size: 1
})

const iconRender = computed(() => (props.icon ?? props.name) as IconType)

const { isMobile } = useDevice()

const computedSize = computed(() => {
	if (isMobile.value && props.mobileSize !== undefined) {
		return props.mobileSize
	}
	return props.size
})

const iconRenderType = computed(() => {
	if (props.fontAwesome && (FONT_AWESOME_ICONS.includes(iconRender.value) || FONT_AWESOME_BRANDS_ICONS.includes(iconRender.value))) { return 'font-awesome' }
	if (MATERIAL_ICONS.includes(iconRender.value)) { return 'material' }
	if (FONT_AWESOME_ICONS.includes(iconRender.value) || FONT_AWESOME_BRANDS_ICONS.includes(iconRender.value)) { return 'font-awesome' }
	return 'material'
})

const isFaBrand = computed(() => FONT_AWESOME_BRANDS_ICONS.includes(iconRender.value))

// Hide until required font is ready to avoid ligature text/fallback flash
const isMaterialReady = ref(true)
const isFAFreeReady = ref(true)
const isFABrandsReady = ref(true)

function getFontFaceSet(): FontFaceSet | null {
	if (typeof document === 'undefined') { return null }
	const maybeFonts = (document as unknown as { fonts?: FontFaceSet }).fonts
	return maybeFonts ?? null
}

function supportsFontLoading(): boolean {
	const fonts = getFontFaceSet()
	return fonts !== null && typeof fonts.load === 'function'
}

async function ensureFontLoaded(family: string): Promise<boolean> {
	if (!supportsFontLoading()) { return true }
	try {
		const fonts = getFontFaceSet()
		if (!fonts) { return true }
		if (fonts.check(`1em "${family}"`)) { return true }
		await fonts.load(`1em "${family}"`)
		return fonts.check(`1em "${family}"`)
	} catch {
		return false
	}
}

async function loadMaterialIfNeeded() {
	const ok = await ensureFontLoaded('Material Symbols Outlined')
	isMaterialReady.value = ok
}

async function loadFAFreeIfNeeded() {
	const ok = await ensureFontLoaded('Font Awesome 6 Free')
	isFAFreeReady.value = ok
}

async function loadFABrandsIfNeeded() {
	const ok = await ensureFontLoaded('Font Awesome 6 Brands')
	isFABrandsReady.value = ok
}

onMounted(() => {
	if (!supportsFontLoading()) { return }
	if (iconRenderType.value === 'material') {
		const fonts = getFontFaceSet()
		isMaterialReady.value = fonts ? fonts.check('1em "Material Symbols Outlined"') : true
		void loadMaterialIfNeeded()
	} else if (isFaBrand.value) {
		const fonts = getFontFaceSet()
		isFABrandsReady.value = fonts ? fonts.check('1em "Font Awesome 6 Brands"') : true
		void loadFABrandsIfNeeded()
	} else {
		const fonts = getFontFaceSet()
		isFAFreeReady.value = fonts ? fonts.check('1em "Font Awesome 6 Free"') : true
		void loadFAFreeIfNeeded()
	}
})

watchEffect(() => {
	if (!supportsFontLoading()) { return }
	if (iconRenderType.value === 'material') {
		void loadMaterialIfNeeded()
	} else if (isFaBrand.value) {
		void loadFABrandsIfNeeded()
	} else {
		void loadFAFreeIfNeeded()
	}
})

const isCurrentFontReady = computed(() => {
	if (iconRenderType.value === 'material') { return isMaterialReady.value }
	return isFaBrand.value ? isFABrandsReady.value : isFAFreeReady.value
})
</script>

<template>
	<span
		v-if="iconRenderType === 'material'" class="bgl_icon-font notranslate"
		:class="{ 'round flex aspect-ratio-1 justify-content-center': round }" :style="{
			'fontSize': `${computedSize}rem`,
			color,
			'font-variation-settings': `'FILL' ${fill ? 1 : 0}, 'wght' ${weight || 400}`,
			'width': round ? `calc(${computedSize}rem * 2)` : 'auto',
			'height': round ? `calc(${computedSize}rem * 2)` : 'auto',
			'visibility': isCurrentFontReady ? 'visible' : 'hidden',
		}" translate="no"
	>
		{{ iconRender }}
	</span>
	<span
		v-else-if="iconRenderType === 'font-awesome'" class="fa bgl_icon-font notranslate" :class="[
			`fa-${iconRender}`,
			{
				'fa-brands': isFaBrand,
				'fa-solid': fill,
				'far': !fill && !isFaBrand,
			},
		]" :style="{ 'fontSize': `${computedSize}rem`, color, 'font-variation-settings': `'wght' ${weight || 400}`, 'visibility': isCurrentFontReady ? 'visible' : 'hidden' }"
		translate="no"
	/>
</template>

<style>
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css');

[dir='rtl'] .data-row .bgl_icon-font,
[dir='rtl'] .embedded-field .bgl_icon-font,
[dir='rtl'] .rich-text-editor .toolbar .bgl_icon-font {
	transform: rotateY(180deg) !important;
}
</style>

<style>
.bgl_icon-font {
	font-family: 'Material Symbols Outlined', serif !important;
}

.bgl_icon-font.fa {
	font-family: 'Font Awesome 6 Free', serif !important;
}

.bgl_icon-font.fa-brands {
	font-family: 'Font Awesome 6 Brands', serif !important;
}
</style>

<!-- <style src="./font-awesome.css" /> -->
