<script setup lang="ts">
import { NInput, NPopover, NSpin } from 'naive-ui'
import { computed, nextTick, ref, watch } from 'vue'

import type { OciConfigSummary } from '@/types/api'

/** 租户选择器:Popover 面板(搜索 + 左分组导航 + 右租户列表),替代平铺下拉。
 *  单选(v-model:value)点行即选即关;多选(multiple + v-model:values)点行切换勾选。
 *  搜索命中时忽略左侧分组限定。 */
const props = withDefaults(
  defineProps<{
    configs: OciConfigSummary[]
    value?: number | null
    values?: number[]
    multiple?: boolean
    clearable?: boolean
    placeholder?: string
    size?: 'small' | 'medium'
    loading?: boolean
    placement?: 'bottom-start' | 'bottom-end'
  }>(),
  { value: null, values: () => [], placeholder: '选择租户', size: 'medium', placement: 'bottom-start' },
)
const emit = defineEmits<{ 'update:value': [number | null]; 'update:values': [number[]] }>()

/** 「未分组」导航项哨兵(空串已被「全部租户」占用) */
const UNGROUPED = '__ungrouped__'

const show = ref(false)
const query = ref('')
const activeGroup = ref('')
const searchInput = ref<InstanceType<typeof NInput> | null>(null)

watch(show, (v) => {
  if (!v) return
  query.value = ''
  activeGroup.value = ''
  void nextTick(() => searchInput.value?.focus())
})

const groups = computed(() => {
  const counts = new Map<string, number>()
  let ungrouped = 0
  for (const c of props.configs) {
    if (c.group) counts.set(c.group, (counts.get(c.group) ?? 0) + 1)
    else ungrouped++
  }
  const out = [{ key: '', label: '全部租户', count: props.configs.length }]
  for (const g of [...counts.keys()].sort((a, b) => a.localeCompare(b, 'zh-CN')))
    out.push({ key: g, label: g, count: counts.get(g) ?? 0 })
  if (ungrouped && counts.size) out.push({ key: UNGROUPED, label: '未分组', count: ungrouped })
  return out
})

const filtered = computed(() => {
  const kw = query.value.trim().toLowerCase()
  if (kw)
    return props.configs.filter(
      (c) => c.alias.toLowerCase().includes(kw) || c.tenancyName.toLowerCase().includes(kw),
    )
  if (!activeGroup.value) return props.configs
  if (activeGroup.value === UNGROUPED) return props.configs.filter((c) => !c.group)
  return props.configs.filter((c) => c.group === activeGroup.value)
})

const selectedIds = computed(() => {
  if (props.multiple) return new Set(props.values)
  return new Set(props.value === null ? [] : [props.value])
})

const displayText = computed(() => {
  if (props.multiple) return props.values.length ? `${props.values.length} 个租户` : ''
  if (props.value === null) return ''
  const cfg = props.configs.find((c) => c.id === props.value)
  return cfg?.alias ?? `#${props.value}`
})

function pick(cfg: OciConfigSummary) {
  if (!props.multiple) {
    emit('update:value', cfg.id)
    show.value = false
    return
  }
  const cur = [...props.values]
  const i = cur.indexOf(cfg.id)
  if (i >= 0) cur.splice(i, 1)
  else cur.push(cfg.id)
  emit('update:values', cur)
}

function clear(e: MouseEvent) {
  e.stopPropagation()
  if (props.multiple) emit('update:values', [])
  else emit('update:value', null)
}

const showClear = computed(
  () => !!props.clearable && (props.multiple ? props.values.length > 0 : props.value !== null),
)
</script>

<template>
  <NPopover v-model:show="show" trigger="click" :placement="placement" raw :show-arrow="false">
    <template #trigger>
      <slot name="trigger">
        <button
          type="button"
          class="group flex w-full cursor-pointer items-center gap-2 rounded-lg border bg-white px-3 text-left transition-colors"
          :class="[
            size === 'small' ? 'h-7' : 'h-[34px]',
            show ? 'border-accent ring-2 ring-accent/15' : 'border-line hover:border-accent/60',
          ]"
        >
          <span
            class="min-w-0 flex-1 truncate"
            :class="[size === 'small' ? 'text-[13px]' : 'text-sm', displayText ? '' : 'text-ink-3']"
          >
            {{ displayText || placeholder }}
          </span>
          <svg
            v-if="showClear"
            class="hidden h-3.5 w-3.5 flex-none text-ink-3 hover:text-ink group-hover:block"
            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"
            stroke-linecap="round" stroke-linejoin="round"
            @click="clear"
          >
            <path d="M18 6 6 18M6 6l12 12" />
          </svg>
          <svg
            class="h-3.5 w-3.5 flex-none text-ink-3 transition-transform"
            :class="[showClear ? 'group-hover:hidden' : '', show ? 'rotate-180' : '']"
            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"
            stroke-linecap="round" stroke-linejoin="round"
          >
            <path d="m6 9 6 6 6-6" />
          </svg>
        </button>
      </slot>
    </template>

    <div class="w-[min(560px,92vw)] overflow-hidden rounded-xl border border-line bg-card shadow-[0_18px_44px_rgba(20,20,19,0.16)]">
      <div class="px-3.5 pt-3 pb-2.5">
        <NInput ref="searchInput" v-model:value="query" size="small" placeholder="搜索租户别名 / 租户名称…" clearable>
          <template #prefix>
            <svg class="h-3.5 w-3.5 text-ink-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
              <circle cx="11" cy="11" r="7" />
              <path d="m20 20-3.5-3.5" />
            </svg>
          </template>
        </NInput>
      </div>
      <NSpin :show="!!loading" size="small">
        <div class="flex min-h-[248px] border-t border-line-soft">
          <div class="flex w-[150px] flex-none flex-col gap-0.5 overflow-y-auto border-r border-line-soft bg-wash p-2 max-md:w-[110px]">
            <button
              v-for="g in groups"
              :key="g.key"
              type="button"
              class="flex cursor-pointer items-center justify-between gap-1.5 rounded-[7px] px-2.5 py-1.5 text-left text-[12.5px]"
              :class="!query && activeGroup === g.key ? 'border border-line bg-card font-semibold' : 'border border-transparent text-ink-2 hover:bg-row-hover'"
              @click="activeGroup = g.key"
            >
              <span class="truncate">{{ g.label }}</span>
              <span class="flex-none text-[11px]" :class="!query && activeGroup === g.key ? 'text-accent' : 'text-ink-3'">{{ g.count }}</span>
            </button>
          </div>
          <div class="flex max-h-[300px] min-w-0 flex-1 flex-col gap-[3px] overflow-y-auto p-2">
            <button
              v-for="c in filtered"
              :key="c.id"
              type="button"
              class="flex cursor-pointer items-center gap-2.5 rounded-lg px-3 py-2 text-left"
              :class="selectedIds.has(c.id) ? 'bg-accent/8 outline outline-1 outline-accent/30' : 'hover:bg-row-hover'"
              @click="pick(c)"
            >
              <span class="min-w-0 flex-1">
                <span class="block truncate text-[13px] font-semibold">{{ c.alias }}</span>
                <span class="mono mt-px block truncate text-[11px] text-ink-3">{{ c.tenancyName }}</span>
              </span>
              <span class="mono flex-none rounded-[5px] border border-line bg-white px-2 py-0.5 text-[11px] text-ink-2">{{ c.region }}</span>
              <span class="w-4 flex-none text-center font-bold text-accent">{{ selectedIds.has(c.id) ? '✓' : '' }}</span>
            </button>
            <div v-if="!filtered.length" class="py-8 text-center text-xs text-ink-3">
              {{ query ? '无匹配租户' : '该分组暂无租户' }}
            </div>
          </div>
        </div>
      </NSpin>
      <div
        v-if="multiple"
        class="flex items-center justify-between border-t border-line-soft bg-wash px-3.5 py-2 text-[11.5px] text-ink-3"
      >
        <span>已选 {{ selectedIds.size }} 个 · 留空表示全部租户</span>
        <button type="button" class="cursor-pointer text-accent hover:underline" @click="clear">清空</button>
      </div>
    </div>
  </NPopover>
</template>
