<script setup lang="ts">
import type { IconifyIcon } from '@iconify/vue'
import { Icon as Iconify } from '@iconify/vue/dist/offline'
import { loadIcon } from '@iconify/vue'
import type { PropType } from 'vue'
import { computed, ref, useNuxtApp, useState, watch } from '#imports'

const props = defineProps({
  size: {
    type: String,
    default: '16px',
  },
  type: {
    type: String as PropType<'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger'>,
    default: 'primary',
  },
  name: {
    type: String,
    required: true,
  },
  color: {
    type: String,
    default: null,
  },
})

const nuxtApp = useNuxtApp()
const state = useState('icons', () => ({}))
const isFetching = ref(false)
const icon = computed<IconifyIcon | null>(() => state.value?.[props.name])
const component = computed(() => nuxtApp.vueApp.component(props.name))

async function loadIconComponent() {
  if (component.value) {
    return
  }
  if (!state.value?.[props.name]) {
    isFetching.value = true
    state.value[props.name] = await loadIcon(props.name).catch(() => {})
    isFetching.value = false
  }
}

watch(() => props.name, loadIconComponent)

!component.value && (await loadIconComponent())
</script>

<template>
  <span class="icon">
    <span v-if="isFetching" />
    <Iconify
      v-else-if="icon"
      :icon="icon"
      :style="{
        color: color ? color : 'inherit',
        width: size,
        height: size,
      }"
    />
    <Component :is="component" v-else-if="component" />
    <span v-else>{{ name }}</span>
  </span>
</template>

<style scoped lang="ts">
css({
  '.icon': {
    display: 'inline-flex',
    variants: {
      primary: {
        backgroundColor: '{color.primary.600}',
        color: '{color.primary.200}',
        ':hover': {
          backgroundColor: '{color.primary.400}'
        }
      },
      secondary: {
        backgroundColor: '{color.gray.300}',
        color: 'black',
        ':hover': {
          backgroundColor: '{color.gray.100}'
        }
      },
      success: {
        backgroundColor: '{color.green.400}',
        color: '{color.green.900}',
        ':hover': {
          backgroundColor: '{color.green.200}'
        }
      },
      info: {
        backgroundColor: '{color.blue.400}',
        color: '{color.blue.900}',
        ':hover': {
          backgroundColor: '{color.blue.200}'
        }
      },
      warning: {
        backgroundColor: '{color.yellow.400}',
        color: '{color.yellow.900}',
        ':hover': {
          backgroundColor: '{color.yellow.200}'
        }
      }
    }
  }
})
</style>
