<template>
  <div
    class="platform-icon-cell"
    :style="{ width: size + 'px' }"
    @click="$emit('click')"
  >
    <div
      class="platform-icon"
      :class="{ 'has-remote-icon': displayResolvedUrl && !imageLoadFailed }"
      :style="{
        width: size + 'px',
        height: size + 'px',
        background: showFallback ? backgroundColor : '#f0f0f0',
        borderRadius: borderRadius + 'px',
      }"
    >
      <img
        v-if="displayResolvedUrl && !imageLoadFailed"
        class="icon-image"
        :src="displayResolvedUrl"
        alt=""
        @error="onImgError"
      />
      <div
        v-if="showFallback"
        class="icon-content"
        :style="{
          padding: (size * 0.12) + 'px',
        }"
      >
        <span
          class="icon-text"
          :style="{
            fontSize: fontSize + 'px',
            lineHeight: '1.4',
            textShadow: '0 1px 2px rgba(0,0,0,0.15)',
          }"
        >
          {{ text }}
        </span>
      </div>
    </div>
    <div
      v-if="showCaption && displayResolvedUrl && !imageLoadFailed"
      class="platform-icon-caption"
    >
      {{ text }}
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import { stringToColor, calculateFontSize } from '../utils/color'
import { isBlobDisplayUrl, resolveLogoDisplayUrl } from '../utils/appIconCache'

const props = withDefaults(
  defineProps<{
    text: string
    size: number
    /** 解析后的 Logo URL；加载失败时回退为文字渐变图标 */
    iconUrl?: string | null
    /** 与应用缓存键前缀一起使用（MinIO icon 时需传） */
    appId?: string | null
    /** 对象存储 object key（非 http(s)） */
    iconObjectKey?: string | null
    /** 是否在图标下方展示应用名称 */
    showCaption?: boolean
    /** rounded：快捷导航类圆角矩形；circle：正圆（消息中心等） */
    shape?: 'rounded' | 'circle'
  }>(),
  {
    showCaption: true,
    shape: 'rounded',
  }
)

defineEmits(['click'])

const imageLoadFailed = ref(false)
const displayResolvedUrl = ref('')
let resolveGeneration = 0

function revokeIfBlob(url: string) {
  if (isBlobDisplayUrl(url)) {
    URL.revokeObjectURL(url)
  }
}

function resetDisplayResolved() {
  revokeIfBlob(displayResolvedUrl.value)
  displayResolvedUrl.value = ''
}

async function refreshDisplayUrl() {
  const gen = ++resolveGeneration
  imageLoadFailed.value = false

  const raw = props.iconUrl?.trim() ?? ''
  resetDisplayResolved()

  if (!raw) {
    return
  }

  if (!props.iconObjectKey?.trim()) {
    displayResolvedUrl.value = raw
    return
  }

  try {
    const url = await resolveLogoDisplayUrl({
      appId: props.appId?.trim() || '_',
      iconObjectKey: props.iconObjectKey,
      iconUrl: raw,
    })
    if (gen !== resolveGeneration) {
      revokeIfBlob(url)
      return
    }
    displayResolvedUrl.value = url
  } catch {
    if (gen !== resolveGeneration) {
      return
    }
    displayResolvedUrl.value = raw
  }
}

watch(
  () => [
    props.iconUrl,
    props.iconObjectKey,
    props.appId,
  ],
  refreshDisplayUrl,
  { immediate: true }
)

onBeforeUnmount(() => {
  ++resolveGeneration
  resetDisplayResolved()
})

const onImgError = () => {
  revokeIfBlob(displayResolvedUrl.value)
  displayResolvedUrl.value = ''
  imageLoadFailed.value = true
}

const showFallback = computed(
  () => !displayResolvedUrl.value || imageLoadFailed.value
)

const backgroundColor = computed(() => stringToColor(props.text))

const borderRadius = computed(() =>
  props.shape === 'circle' ? props.size / 2 : props.size * 0.22
)

const fontSize = computed(() => calculateFontSize(props.size, props.text.length))
</script>

<style scoped>
.platform-icon-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
  user-select: none;
}

.platform-icon {
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
  box-shadow:
    0 4px 6px -1px rgba(0, 0, 0, 0.1),
    0 2px 4px -1px rgba(0, 0, 0, 0.06);
  position: relative;
  overflow: hidden;
}

.icon-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  display: block;
}

.platform-icon:not(.has-remote-icon)::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0) 100%);
  pointer-events: none;
  z-index: 1;
}

.platform-icon:hover {
  transform: translateY(-2px) scale(1.02);
  box-shadow:
    0 10px 15px -3px rgba(0, 0, 0, 0.1),
    0 4px 6px -2px rgba(0, 0, 0, 0.05);
}

.platform-icon:active {
  transform: translateY(0) scale(0.96);
  box-shadow:
    0 2px 4px -1px rgba(0, 0, 0, 0.1),
    0 1px 2px -1px rgba(0, 0, 0, 0.06);
}

.platform-icon-caption {
  margin-top: 8px;
  width: 100%;
  max-width: 100%;
  text-align: center;
  font-size: 12px;
  line-height: 1.4;
  color: #606266;
  word-break: break-word;
  overflow-wrap: break-word;
  white-space: normal;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

.icon-content {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
  z-index: 2;
}

.icon-text {
  font-weight: 600;
  text-align: center;
  word-break: break-word;
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  letter-spacing: 0.5px;
}
</style>
