<script setup lang="ts">
import { NButton, NDataTable, NInput, NSelect, NTooltip, useDialog, type DataTableColumns } from 'naive-ui'
import { computed, h, reactive, ref } from 'vue'

import { createAiChannel, deleteAiChannel, probeAiChannel, updateAiChannel } from '@/api/aigateway'
import { listCachedRegions } from '@/api/configs'
import AiChannelModelsModal from '@/components/ai/AiChannelModelsModal.vue'
import GroupChips from '@/components/ai/GroupChips.vue'
import PanelHeader from '@/components/PanelHeader.vue'
import AppInputNumber from '@/components/AppInputNumber.vue'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import TenantPicker from '@/components/TenantPicker.vue'
import { useAsync } from '@/composables/useAsync'
import { fmtRelative } from '@/composables/useFormat'
import { useToast } from '@/composables/useToast'
import { useScopeStore } from '@/stores/scope'
import type { AiChannel, AiProbeStatus } from '@/types/api'

const props = defineProps<{ channels: AiChannel[]; loading: boolean }>()
const emit = defineEmits<{ changed: [] }>()

const groupHints = computed(() => [...new Set(props.channels.map((c) => c.group).filter(Boolean))])

const toast = useToast()
const dialog = useDialog()
const scope = useScopeStore()

const PROBE_META: Record<Exclude<AiProbeStatus, ''>, { kind: 'run' | 'warn' | 'term' | 'stop'; label: string }> = {
  ok: { kind: 'run', label: '可用' },
  no_service: { kind: 'warn', label: '无服务' },
  no_quota: { kind: 'warn', label: '无配额' },
  error: { kind: 'term', label: '不可用' },
}

// ---- 新建 / 编辑弹窗 ----
const showForm = ref(false)
const editing = ref<AiChannel | null>(null)
const submitting = ref(false)
const form = reactive({ ociConfigId: null as number | null, region: '', name: '', group: '', priority: 1, weight: 1 })

const regions = useAsync(
  () => (form.ociConfigId === null ? Promise.resolve([]) : listCachedRegions(form.ociConfigId).catch(() => [])),
  false,
)

const regionOptions = computed(() =>
  (regions.data.value ?? []).map((r) => ({ label: r.name, value: r.name })),
)

function onCfgChange() {
  form.region = ''
  void regions.run()
}

function openCreate() {
  editing.value = null
  Object.assign(form, { ociConfigId: null, region: '', name: '', group: '', priority: 1, weight: 1 })
  showForm.value = true
}

function openEdit(ch: AiChannel) {
  editing.value = ch
  Object.assign(form, { ociConfigId: ch.ociConfigId, region: ch.region, name: ch.name, group: ch.group, priority: ch.priority, weight: ch.weight })
  showForm.value = true
}

async function submit() {
  submitting.value = true
  try {
    if (editing.value) {
      await updateAiChannel(editing.value.id, { name: form.name, group: form.group, priority: form.priority, weight: form.weight })
      toast.success('已更新渠道')
      showForm.value = false
      emit('changed')
    } else {
      const ch = await createAiChannel({
        ociConfigId: form.ociConfigId ?? undefined, region: form.region, name: form.name || undefined,
        group: form.group, priority: form.priority, weight: form.weight,
      })
      showForm.value = false
      emit('changed')
      toast.success('渠道已添加,正在探测…')
      void doProbe(ch.id)
    }
  } catch (e) {
    toast.error(e instanceof Error ? e.message : '保存失败')
  } finally {
    submitting.value = false
  }
}

const probing = ref(new Set<number>())

async function doProbe(id: number) {
  probing.value.add(id)
  try {
    const ch = await probeAiChannel(id)
    const meta = ch.probeStatus ? PROBE_META[ch.probeStatus as Exclude<AiProbeStatus, ''>] : null
    if (ch.probeStatus === 'ok') {
      toast.success('探测完成:可用')
    } else {
      toast.warning(`探测完成:${meta?.label ?? ch.probeStatus}`, ch.probeError || undefined)
    }
  } catch (e) {
    toast.error(e instanceof Error ? e.message : '探测失败')
  } finally {
    probing.value.delete(id)
    emit('changed')
  }
}

// ---- 模型列表弹窗 ----
const showModels = ref(false)
const modelsChannel = ref<AiChannel | null>(null)

function openModels(ch: AiChannel) {
  modelsChannel.value = ch
  showModels.value = true
}

async function toggle(ch: AiChannel) {
  try {
    await updateAiChannel(ch.id, { enabled: !ch.enabled })
    toast.success(ch.enabled ? '已停用' : '已启用')
    emit('changed')
  } catch (e) {
    toast.error(e instanceof Error ? e.message : '操作失败')
  }
}

function confirmRemove(ch: AiChannel) {
  dialog.warning({
    title: '删除渠道',
    content: `删除渠道「${ch.name}」及其模型缓存?`,
    positiveText: '删除',
    negativeText: '取消',
    onPositiveClick: async () => {
      await deleteAiChannel(ch.id)
      toast.success('已删除')
      emit('changed')
    },
  })
}

function probeCell(r: AiChannel) {
  const badge = probeBadge(r)
  const tip = [r.probeError, r.lastProbeAt ? `探测于 ${fmtRelative(r.lastProbeAt)}` : '']
    .filter(Boolean)
    .join('\n')
  if (!tip) return badge
  return h(
    NTooltip,
    { style: { maxWidth: '380px' } },
    {
      trigger: () => h('span', { class: 'inline-flex cursor-help' }, [badge]),
      default: () => h('span', { class: 'text-xs leading-relaxed whitespace-pre-wrap break-all' }, tip),
    },
  )
}

function probeBadge(r: AiChannel) {
  if (!r.enabled) return h(StatusBadge, { kind: 'stop', label: '已停用' })
  if (r.disabledUntil && new Date(r.disabledUntil) > new Date())
    return h(StatusBadge, { kind: 'warn', label: '熔断中' })
  const meta = r.probeStatus ? PROBE_META[r.probeStatus as Exclude<AiProbeStatus, ''>] : null
  return meta ? h(StatusBadge, meta) : h('span', { class: 'text-xs text-ink-3' }, '未探测')
}

const columns = computed<DataTableColumns<AiChannel>>(() => [
  { title: '名称', key: 'name', minWidth: 200, ellipsis: { tooltip: true }, render: (r) => h('span', { class: 'text-[13px] font-medium' }, r.name) },
  { title: '区域', key: 'region', width: 150, ellipsis: { tooltip: true }, render: (r) => h('span', { class: 'mono text-xs text-ink-2' }, r.region) },
  {
    title: '分组', key: 'group', width: 100,
    render: (r) => r.group
      ? h('span', { class: 'rounded-[5px] bg-info/15 px-2 py-0.5 text-xs font-medium text-info' }, r.group)
      : h('span', { class: 'text-xs text-ink-3' }, '默认'),
  },
  { title: '优先级', key: 'priority', width: 76, render: (r) => h('span', { class: 'mono text-[13px]' }, String(r.priority)) },
  { title: '权重', key: 'weight', width: 66, render: (r) => h('span', { class: 'mono text-[13px]' }, String(r.weight)) },
  {
    title: '模型数量', key: 'modelCount', width: 86,
    render: (r) => h('span', { class: r.modelCount ? 'mono text-[13px]' : 'text-xs text-ink-3' }, String(r.modelCount ?? 0)),
  },
  { title: '探测状态', key: 'probeStatus', width: 120, render: probeCell },
  {
    title: '操作', key: 'actions', width: 250,
    render: (r) => h('div', { class: 'flex items-center gap-0.5' }, [
      h(NButton, { size: 'tiny', quaternary: true, type: 'primary', loading: probing.value.has(r.id), onClick: () => doProbe(r.id) }, { default: () => '探测' }),
      h(NButton, { size: 'tiny', quaternary: true, type: 'primary', onClick: () => openModels(r) }, { default: () => '模型列表' }),
      h(NButton, { size: 'tiny', quaternary: true, onClick: () => toggle(r) }, { default: () => (r.enabled ? '停用' : '启用') }),
      h(NButton, { size: 'tiny', quaternary: true, type: 'primary', onClick: () => openEdit(r) }, { default: () => '编辑' }),
      h(NButton, { size: 'tiny', quaternary: true, type: 'error', onClick: () => confirmRemove(r) }, { default: () => '删除' }),
    ]),
  },
])
</script>

<template>
  <div class="panel">
    <PanelHeader
      title="渠道(号池)"
      desc="渠道 = 租户 × 区域;请求按优先级分组、组内权重随机,429/超时自动换渠道重试,连续失败指数退避熔断"
    >
      <NButton size="small" type="primary" @click="openCreate">添加渠道</NButton>
    </PanelHeader>
    <NDataTable :columns="columns" :data="channels" :loading="loading" :bordered="false" size="small" />
    <div class="border-t border-line-soft bg-wash px-4.5 py-2.5 text-xs leading-relaxed text-ink-3">
      探测三步:列模型(服务可见性)→ 同步模型缓存 → max_tokens=16 试调(配额)· 探测成功自动复位熔断 ·
      有渠道时系统自动维护「AI渠道探测」后台任务(每天 00:00 逐渠道探测,渠道清空自动暂停)
    </div>

    <AiChannelModelsModal
      v-model:show="showModels"
      :channel="modelsChannel"
      @changed="emit('changed')"
    />

    <FormModal
      v-model:show="showForm"
      :title="editing ? '编辑渠道' : '添加渠道'"
      :submitting="submitting"
      :submit-text="editing ? '保存' : '添加并探测'"
      :submit-disabled="!editing && (form.ociConfigId === null || !form.region)"
      @submit="submit"
    >
      <template v-if="!editing">
        <FormField label="租户配置" required>
          <TenantPicker
            v-model:value="form.ociConfigId"
            :configs="scope.configs.data ?? []"
            :loading="scope.configs.loading"
            @update:value="onCfgChange"
          />
        </FormField>
        <FormField label="区域" required hint="建议选 GenAI 提供区域:芝加哥/阿什本/凤凰城/法兰克福/伦敦/大阪等;租户需已订阅该区域">
          <NSelect v-model:value="form.region" :options="regionOptions" :loading="regions.loading.value" placeholder="选择区域" filterable tag />
        </FormField>
      </template>
      <FormField v-else label="名称">
        <NInput v-model:value="form.name" />
      </FormField>
      <div class="grid grid-cols-3 gap-3">
        <FormField label="分组" hint="分组供密钥定向路由;留空归入默认分组">
          <NInput v-model:value="form.group" placeholder="默认" />
          <GroupChips :groups="groupHints" @pick="form.group = $event" />
        </FormField>
        <FormField label="优先级" hint="数字小优先">
          <AppInputNumber v-model:value="form.priority" :min="1" />
        </FormField>
        <FormField label="权重" hint="同优先级内按权重分流">
          <AppInputNumber v-model:value="form.weight" :min="1" />
        </FormField>
      </div>
      <div v-if="!editing" class="rounded-lg bg-wash px-3 py-2 text-xs leading-relaxed text-ink-3">
        创建后自动发起一次探测,并自动维护「AI渠道探测」后台任务(渠道清空自动暂停)
      </div>
    </FormModal>
  </div>
</template>
