<script setup lang="ts">
import { NButton, NInput, NModal, useDialog } from 'naive-ui'
import { computed, ref, watch } from 'vue'

import {
  addAiBlacklist,
  listAiChannelModels,
  syncAiChannelModels,
  testAiChannelModel,
} from '@/api/aigateway'
import { useMobileModal } from '@/composables/useMobileModal'
import { useToast } from '@/composables/useToast'
import type { AiChannel, AiModelCacheItem } from '@/types/api'

const props = defineProps<{ show: boolean; channel: AiChannel | null }>()
const emit = defineEmits<{ 'update:show': [boolean]; changed: [] }>()

const toast = useToast()
const dialog = useDialog()
const { modalStyle, modalClass } = useMobileModal(640)

const rows = ref<AiModelCacheItem[]>([])
const loading = ref(false)
const syncing = ref(false)
const testing = ref('')
const filter = ref('')
/** 本地镜像验证模型:测试通过即时高亮,不等父级刷新 */
const probeModel = ref('')

const CAP_ORDER = ['CHAT', 'EMBEDDING', 'RERANK', 'TTS']
const CAP_LABEL: Record<string, string> = { CHAT: '对话', EMBEDDING: '向量', RERANK: '重排', TTS: '语音' }

/** 按能力分组 + 关键字过滤;仅对话组提供「测试」入口 */
const groups = computed(() => {
  const kw = filter.value.trim().toLowerCase()
  const buckets = new Map<string, AiModelCacheItem[]>()
  for (const r of rows.value) {
    if (kw && !r.name.toLowerCase().includes(kw)) continue
    const cap = r.capability || 'CHAT'
    buckets.set(cap, [...(buckets.get(cap) ?? []), r])
  }
  const order = [...CAP_ORDER, ...[...buckets.keys()].filter((c) => !CAP_ORDER.includes(c))]
  return order
    .filter((cap) => buckets.has(cap))
    .map((cap) => ({ cap, label: CAP_LABEL[cap] ?? cap, items: buckets.get(cap)! }))
})

const shownCount = computed(() => groups.value.reduce((n, g) => n + g.items.length, 0))

watch(
  () => props.show,
  (show) => {
    if (show && props.channel) {
      probeModel.value = props.channel.probeModel
      filter.value = ''
      void reload()
    }
  },
)

async function reload() {
  if (!props.channel) return
  loading.value = true
  try {
    rows.value = await listAiChannelModels(props.channel.id)
  } catch (e) {
    toast.error(e instanceof Error ? e.message : '加载失败')
  } finally {
    loading.value = false
  }
}

async function doSync() {
  if (!props.channel) return
  syncing.value = true
  try {
    rows.value = await syncAiChannelModels(props.channel.id)
    toast.success(`已同步 ${rows.value.length} 个模型`)
    emit('changed')
  } catch (e) {
    toast.error(e instanceof Error ? e.message : '同步失败')
  } finally {
    syncing.value = false
  }
}

async function doTest(name: string) {
  if (!props.channel || testing.value) return
  testing.value = name
  try {
    const ch = await testAiChannelModel(props.channel.id, name)
    probeModel.value = ch.probeModel
    toast.success('测试通过', '已设为该渠道探测验证模型,后续探测优先使用')
    emit('changed')
  } catch (e) {
    toast.error(e instanceof Error ? e.message : '测试失败')
  } finally {
    testing.value = ''
  }
}

function confirmBlacklist(name: string) {
  dialog.warning({
    title: '拉黑模型',
    content: `「${name}」将从全部渠道剔除,同步与探测不再入库;可在模型黑名单中移出后重新同步恢复。`,
    positiveText: '拉黑',
    negativeText: '取消',
    onPositiveClick: async () => {
      try {
        await addAiBlacklist(name)
        toast.success('已拉黑')
        void reload()
        emit('changed')
      } catch (e) {
        toast.error(e instanceof Error ? e.message : '拉黑失败')
      }
    },
  })
}
</script>

<template>
  <NModal
    :show="show"
    preset="card"
    :style="modalStyle"
    :class="modalClass"
    @update:show="emit('update:show', $event)"
  >
    <template #header>
      <div>
        <div class="text-[15px] leading-tight font-semibold">模型列表</div>
        <div class="mono mt-1 text-xs font-normal text-ink-3">{{ channel?.name ?? '' }}</div>
      </div>
    </template>

    <div class="flex items-center gap-2 pb-2">
      <NInput v-model:value="filter" size="small" placeholder="过滤模型名…" clearable class="min-w-0 flex-1" />
      <span class="flex-none text-xs whitespace-nowrap text-ink-3">
        {{ filter ? `${shownCount} / ${rows.length}` : `${rows.length} 个模型` }}
      </span>
      <NButton size="small" :loading="syncing" @click="doSync">同步模型</NButton>
    </div>

    <div class="-mx-2 max-h-[58vh] overflow-y-auto">
      <div v-if="loading" class="py-10 text-center text-xs text-ink-3">加载中…</div>
      <template v-else>
        <template v-for="g in groups" :key="g.cap">
          <div class="px-3 pt-3 pb-1 text-[11.5px] font-semibold tracking-wide text-ink-3">
            {{ g.label }} <span class="font-normal">· {{ g.items.length }}</span>
          </div>
          <div
            v-for="r in g.items"
            :key="r.name"
            class="group flex items-center gap-2 rounded-lg px-3 py-[5px] hover:bg-row-hover"
          >
            <span
              class="mono min-w-0 truncate text-[13px]"
              :class="r.deprecatedAt ? 'text-ink-3' : ''"
              :title="r.modelOcid"
            >
              {{ r.name }}
            </span>
            <span
              v-if="r.name === probeModel"
              class="flex flex-none items-center gap-1 rounded-[5px] bg-info/15 px-1.5 py-px text-[10.5px] font-medium text-info"
            >
              <span class="h-[5px] w-[5px] rounded-full bg-info" />验证模型
            </span>
            <span v-if="r.deprecatedAt" class="flex-none text-[11px] text-warn">弃用</span>
            <span class="min-w-0 flex-1" />
            <button
              v-if="g.cap === 'CHAT'"
              type="button"
              class="flex-none cursor-pointer rounded-[5px] px-1.5 py-0.5 text-xs hover:bg-accent/10"
              :class="testing === r.name ? 'text-accent' : 'text-ink-3/70 group-hover:text-accent'"
              :disabled="!!testing"
              @click="doTest(r.name)"
            >
              {{ testing === r.name ? '测试中…' : '测试' }}
            </button>
            <button
              type="button"
              class="flex-none cursor-pointer rounded-[5px] px-1.5 py-0.5 text-xs text-ink-3/70 group-hover:text-err hover:bg-err/10"
              @click="confirmBlacklist(r.name)"
            >
              拉黑
            </button>
          </div>
        </template>
        <div v-if="!shownCount" class="py-10 text-center text-xs text-ink-3">
          {{ rows.length ? '无匹配模型' : '暂无模型缓存,点击右上「同步模型」拉取' }}
        </div>
      </template>
    </div>

    <div class="mt-1 border-t border-line-soft pt-2.5 text-[11.5px] leading-relaxed text-ink-3">
      <span class="font-medium text-ink-2">测试</span>:发 max_tokens=16
      试调,通过即设为该渠道探测验证模型(后续探测优先使用)并置渠道可用 ·
      <span class="font-medium text-ink-2">拉黑</span>:全局生效,从全部渠道剔除 ·
      非对话模型不提供测试
    </div>
  </NModal>
</template>
