<script setup lang="ts">
import { computed, ref, watch } from 'vue'

type ImageMode =
  | 'scaleToFill'
  | 'aspectFit'
  | 'aspectFill'
  | 'widthFix'
  | 'heightFix'
  | 'top'
  | 'bottom'
  | 'center'
  | 'left'
  | 'right'
  | 'top left'
  | 'top right'
  | 'bottom left'
  | 'bottom right'

interface Props {
  src?: string
  alt?: string
  fallbackSrc?: string
  mode?: ImageMode
  width?: string | number
  height?: string | number
  radius?: string | number
  lazyLoad?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  src: '',
  alt: '点位图片',
  fallbackSrc: '/static/poi/placeholder.png',
  mode: 'aspectFill',
  width: '100%',
  height: '100%',
  radius: 0,
  lazyLoad: true,
})

const emit = defineEmits<{
  load: [event: unknown]
  error: [event: unknown]
  fallback: [failedSrc: string]
}>()

const currentSrc = ref('')
const fallbackFailed = ref(false)
const lastReportedFallbackSource = ref<string | null>(null)

const imageStyle = computed(() => ({
  width: normalizeSize(props.width),
  height: normalizeSize(props.height),
  borderRadius: normalizeSize(props.radius),
}))

function normalizeSize(value: string | number): string {
  return typeof value === 'number' ? `${value}rpx` : value
}

function resetSource(): void {
  const source = props.src.trim()
  const fallback = props.fallbackSrc.trim()
  currentSrc.value = source || fallback
  fallbackFailed.value = !currentSrc.value
}

function handleLoad(event: unknown): void {
  emit('load', event)
}

function handleError(event: unknown): void {
  emit('error', event)

  const fallback = props.fallbackSrc.trim()
  if (fallback && currentSrc.value !== fallback) {
    const failedSrc = currentSrc.value
    currentSrc.value = fallback
    if (lastReportedFallbackSource.value !== failedSrc) {
      lastReportedFallbackSource.value = failedSrc
      emit('fallback', failedSrc)
    }
    return
  }

  fallbackFailed.value = true
}

watch(
  () => props.src,
  () => {
    lastReportedFallbackSource.value = null
    resetSource()
  },
  { immediate: true },
)

watch(() => props.fallbackSrc, resetSource)
</script>

<template>
  <view
    class="poi-image"
    :style="imageStyle"
    role="img"
    :aria-label="alt"
  >
    <image
      v-if="!fallbackFailed"
      class="poi-image__asset"
      :src="currentSrc"
      :mode="mode"
      :lazy-load="lazyLoad"
      :alt="alt"
      @load="handleLoad"
      @error="handleError"
    />
    <view v-else class="poi-image__unavailable">
      <text class="poi-image__unavailable-mark">GM</text>
      <text class="poi-image__unavailable-text">图片暂不可用</text>
    </view>
  </view>
</template>

<style scoped lang="scss">
.poi-image {
  position: relative;
  display: block;
  flex: none;
  overflow: hidden;
  background: #e8eeeb;
}

.poi-image__asset {
  display: block;
  width: 100%;
  height: 100%;
}

.poi-image__unavailable {
  display: flex;
  flex-direction: column;
  gap: 10rpx;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 120rpx;
  color: #6d7d75;
  background: linear-gradient(145deg, #edf3f0, #dce6e1);
}

.poi-image__unavailable-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64rpx;
  height: 64rpx;
  font-size: 22rpx;
  font-weight: 700;
  line-height: 64rpx;
  color: #527064;
  border: 2rpx solid #91a39a;
  border-radius: 50%;
}

.poi-image__unavailable-text {
  font-size: 22rpx;
  line-height: 32rpx;
}
</style>
