<script setup lang="ts">
import { NButton, NPopconfirm, NSpin } from 'naive-ui'

import { computed, ref } from 'vue'

import { addVnicIpv6, detachVnic, listVnics, removeIpv6 } from '@/api/instances'
import FootNote from '@/components/FootNote.vue'
import AttachVnicModal from '@/components/instance/AttachVnicModal.vue'
import LifecycleBadge from '@/components/LifecycleBadge.vue'
import OcidText from '@/components/OcidText.vue'
import { useAsync } from '@/composables/useAsync'
import { ATTACHMENT_TRANSIENT_STATES, useTransientPoll } from '@/composables/useTransientPoll'
import { useToast } from '@/composables/useToast'

/** 网卡(VNIC)区块：每卡一主行 + IPv6 副行；IPv6 增删按卡操作。
 *  删除复用实例级接口（后端按地址遍历全部网卡），添加走 per-VNIC 接口。 */
const props = defineProps<{
  cfgId: number
  instanceId: string
  region?: string
  /** 实例已终止 / 终止中：全部变更操作不可用 */
  disabled?: boolean
}>()

const emit = defineEmits<{ changed: [] }>()

const message = useToast()
const vnics = useAsync(() => listVnics(props.cfgId, props.instanceId, props.region))

/** 附加已受理但 OCI 挂载列表有可见性延迟：限时轮询等新卡入列，入列后交由过渡态轮询接管 */
const expectAtLeast = ref(0)
let expectUntil = 0

useTransientPoll(
  () => vnics.data.value,
  (list) =>
    !!list?.some((v) => ATTACHMENT_TRANSIENT_STATES.includes(v.lifecycleState)) ||
    ((list?.length ?? 0) < expectAtLeast.value && Date.now() < expectUntil),
  () => void vnics.run({ silent: true }),
)

const showAttach = ref(false)
const detaching = ref('')
const addingIpv6 = ref('')
const removingIpv6 = ref('')
const busy = computed(() => !!detaching.value || !!addingIpv6.value || !!removingIpv6.value)

function refresh() {
  void vnics.run()
  emit('changed')
}

function onAttached() {
  expectAtLeast.value = (vnics.data.value ?? []).length + 1
  expectUntil = Date.now() + 90_000
  refresh()
}

async function doDetach(attachmentId: string) {
  detaching.value = attachmentId
  try {
    await detachVnic(props.cfgId, attachmentId, props.region)
    message.success('分离请求已提交')
    refresh()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '分离失败')
  } finally {
    detaching.value = ''
  }
}

async function doAddIpv6(vnicId: string) {
  addingIpv6.value = vnicId
  try {
    await addVnicIpv6(props.cfgId, vnicId, '', props.region)
    message.success('IPv6 已添加')
    refresh()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '添加失败，请确认子网已启用 IPv6')
  } finally {
    addingIpv6.value = ''
  }
}

async function doRemoveIpv6(addr: string) {
  removingIpv6.value = addr
  try {
    await removeIpv6(props.cfgId, props.instanceId, addr, props.region)
    message.success('IPv6 已取消分配')
    refresh()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '删除失败')
  } finally {
    removingIpv6.value = ''
  }
}

defineExpose({ refresh: () => vnics.run() })
</script>

<template>
  <div class="panel">
    <div class="flex items-center justify-between gap-3 border-b border-line-soft px-4.5 py-3.5">
      <div class="min-w-0">
        <div class="text-sm font-semibold">网卡（VNIC）</div>
        <div class="mt-0.5 text-xs text-ink-3">
          实例的全部虚拟网卡；每张网卡可各自附加多个 IPv6
        </div>
      </div>
      <NButton size="small" type="primary" :disabled="disabled" @click="showAttach = true">
        附加 VNIC
      </NButton>
    </div>

    <div
      v-if="vnics.loading.value && !vnics.data.value"
      class="flex items-center justify-center py-8"
    >
      <NSpin size="small" />
    </div>
    <div v-else class="overflow-x-auto">
      <table class="w-full min-w-[760px] border-collapse text-[13px]">
        <thead>
          <tr class="text-left text-xs text-ink-3">
            <th class="w-[26%] border-b border-line-soft py-2 pr-3 pl-4.5 font-medium">名称</th>
            <th class="border-b border-line-soft px-3 py-2 font-medium">私网 IP</th>
            <th class="border-b border-line-soft px-3 py-2 font-medium">公网 IP</th>
            <th class="border-b border-line-soft px-3 py-2 font-medium">子网</th>
            <th class="w-28 border-b border-line-soft px-3 py-2 font-medium">状态</th>
            <th class="w-18 border-b border-line-soft py-2 pr-4.5 pl-3 font-medium">操作</th>
          </tr>
        </thead>
        <tbody>
          <template v-for="v in vnics.data.value ?? []" :key="v.attachmentId">
            <tr>
              <td class="py-2.5 pr-3 pb-1.5 pl-4.5">
                <div class="flex min-w-0 items-center gap-2">
                  <span class="truncate font-medium" :title="v.displayName">
                    {{ v.displayName || '（未命名）' }}
                  </span>
                  <span
                    v-if="v.isPrimary"
                    class="flex-none rounded bg-accent/10 px-1.5 py-px text-[10.5px] font-semibold text-accent"
                  >
                    主网卡
                  </span>
                </div>
              </td>
              <td class="mono px-3 py-2.5 pb-1.5">{{ v.privateIp || '—' }}</td>
              <td class="mono px-3 py-2.5 pb-1.5">{{ v.publicIp || '—' }}</td>
              <td class="px-3 py-2.5 pb-1.5">
                <span v-if="v.subnetName" class="truncate" :title="v.subnetName">
                  {{ v.subnetName }}
                </span>
                <OcidText v-else-if="v.subnetId" :ocid="v.subnetId" />
                <span v-else>—</span>
              </td>
              <td class="px-3 py-2.5 pb-1.5"><LifecycleBadge :state="v.lifecycleState" /></td>
              <td class="py-2.5 pr-4.5 pb-1.5 pl-3">
                <NButton
                  v-if="v.isPrimary"
                  size="tiny"
                  quaternary
                  disabled
                  title="主网卡不可分离"
                >
                  分离
                </NButton>
                <NPopconfirm v-else @positive-click="doDetach(v.attachmentId)">
                  <template #trigger>
                    <NButton
                      size="tiny"
                      quaternary
                      type="error"
                      :loading="detaching === v.attachmentId"
                      :disabled="disabled || v.lifecycleState !== 'ATTACHED' || busy"
                    >
                      分离
                    </NButton>
                  </template>
                  分离该网卡？其上全部 IP（含 IPv6）将一并释放
                </NPopconfirm>
              </td>
            </tr>
            <tr class="border-b border-line-soft last:border-b-0">
              <td colspan="6" class="px-4.5 pt-0 pb-2.5">
                <div class="flex flex-wrap items-center gap-1.5">
                  <span class="text-[10.5px] font-medium tracking-wider text-ink-3">IPV6</span>
                  <template v-if="v.vnicId && v.lifecycleState === 'ATTACHED'">
                    <span
                      v-for="addr in v.ipv6Addresses ?? []"
                      :key="addr"
                      class="mono inline-flex max-w-full items-center gap-1 rounded border border-line bg-white px-1.5 py-0.5 text-xs"
                    >
                      <span class="truncate">{{ addr }}</span>
                      <button
                        class="flex-none cursor-pointer text-ink-3 hover:text-err disabled:cursor-wait"
                        :aria-label="`删除 ${addr}`"
                        :disabled="disabled || busy"
                        @click="doRemoveIpv6(addr)"
                      >
                        {{ removingIpv6 === addr ? '…' : '×' }}
                      </button>
                    </span>
                    <span v-if="!(v.ipv6Addresses ?? []).length" class="text-xs text-ink-3">
                      无
                    </span>
                    <NButton
                      size="tiny"
                      quaternary
                      type="primary"
                      :loading="addingIpv6 === v.vnicId"
                      :disabled="disabled || busy"
                      @click="doAddIpv6(v.vnicId)"
                    >
                      + IPv6
                    </NButton>
                  </template>
                  <span v-else class="text-xs text-ink-3">
                    {{ v.vnicId ? '网卡未在挂载状态,不可操作 IPv6' : '无 · 附加完成后可添加' }}
                  </span>
                </div>
              </td>
            </tr>
          </template>
        </tbody>
      </table>
      <div
        v-if="!(vnics.data.value ?? []).length && !vnics.loading.value"
        class="py-6 text-center text-[13px] text-ink-3"
      >
        {{ vnics.error.value || '无网卡记录' }}
      </div>
    </div>

    <FootNote>
      附加须实例运行中且 shape 有空余 VNIC 配额 · 主网卡不可分离 · 每卡 IPv6
      可多个（要求所在子网已启用 IPv6）
    </FootNote>

    <AttachVnicModal
      v-model:show="showAttach"
      :cfg-id="cfgId"
      :instance-id="instanceId"
      :region="region"
      :attached-count="(vnics.data.value ?? []).length"
      @attached="onAttached"
    />
  </div>
</template>
