<script setup lang="ts">
import { NButton, NDynamicTags, NPopconfirm, NSpin, NSwitch } from 'naive-ui'
import { computed, ref, watch } from 'vue'

import {
  ensureLogWebhook,
  getLogRelay,
  getLogWebhook,
  revokeLogWebhook,
  setupLogRelay,
  teardownLogRelay,
} from '@/api/logevents'
import {
  getIdentitySetting,
  getNotificationRecipients,
  listPasswordPolicies,
  updateIdentitySetting,
  updateNotificationRecipients,
} from '@/api/tenant'
import FootNote from '@/components/FootNote.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import PolicyEditModal from '@/components/tenant/PolicyEditModal.vue'
import { useAsync } from '@/composables/useAsync'
import type { PasswordPolicy } from '@/types/api'
import { useToast } from '@/composables/useToast'

const props = defineProps<{ cfgId: number; domainId?: string }>()

const message = useToast()

const policies = useAsync(() => listPasswordPolicies(props.cfgId, props.domainId))
const recipients = useAsync(() => getNotificationRecipients(props.cfgId, props.domainId))
const identity = useAsync(() => getIdentitySetting(props.cfgId, props.domainId))
const webhook = useAsync(() => getLogWebhook(props.cfgId))
const relay = useAsync(() => getLogRelay(props.cfgId))
const savingIdentity = ref(false)
const savingWebhook = ref(false)
const settingUpRelay = ref(false)
const tearingRelay = ref(false)

// OCI 侧链路四资源,按数据流向排成管线;任一资源存在即视为「已建(或残留)」
const relayRows = computed(() => {
  const d = relay.data.value
  if (!d) return []
  return [
    { label: 'Topic', res: d.topic },
    { label: '订阅 · HTTPS', res: d.subscription },
    { label: 'Policy', res: d.policy },
    { label: 'Connector', res: d.connector },
  ]
})
const relayBuilt = computed(() => relayRows.value.some((r) => r.res.id !== ''))

/** 主卡状态 chip:运行中 / 仅回调地址 / 未启用 */
const relayChip = computed(() => {
  if (relay.data.value?.ready) return { label: '链路运行中', dot: 'bg-ok' }
  if (webhook.data.value?.exists) return { label: '回调地址已生成', dot: 'bg-warn' }
  return { label: '未启用', dot: 'bg-ink-3/50' }
})

function relayDotClass(res: { id: string; state: string }): string {
  if (!res.id) return 'bg-ink-3/40'
  if (res.state === 'ACTIVE' || !res.state) return 'bg-ok'
  if (['PENDING', 'CREATING', 'UPDATING'].includes(res.state)) return 'bg-warn'
  return 'bg-ink-3/40'
}

async function createRelay() {
  settingUpRelay.value = true
  try {
    await setupLogRelay(props.cfgId)
    message.success('回传链路已建立,关键事件将自动回传')
    void relay.run()
    void webhook.run()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '创建失败')
    void relay.run({ silent: true })
  } finally {
    settingUpRelay.value = false
  }
}

async function destroyRelay() {
  tearingRelay.value = true
  try {
    await teardownLogRelay(props.cfgId)
    message.success('回传链路已销毁,回调地址已撤销')
    void relay.run()
    void webhook.run()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '销毁失败')
    void relay.run({ silent: true })
  } finally {
    tearingRelay.value = false
  }
}

const emails = ref<string[]>([])
const saving = ref(false)
const showPolicyEdit = ref(false)
const editingPolicy = ref<PasswordPolicy | null>(null)

function openPolicyEdit(p: PasswordPolicy) {
  editingPolicy.value = p
  showPolicyEdit.value = true
}

watch(
  () => recipients.data.value,
  (data) => {
    if (data) emails.value = [...data.recipients]
  },
)

async function saveRecipients() {
  saving.value = true
  try {
    await updateNotificationRecipients(props.cfgId, emails.value, props.domainId)
    message.success(emails.value.length ? '通知收件人已保存' : '已关闭 test mode，恢复默认发送')
    void recipients.run()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '保存失败')
  } finally {
    saving.value = false
  }
}

async function toggleEmailRequired(v: boolean) {
  savingIdentity.value = true
  try {
    await updateIdentitySetting(props.cfgId, v, props.domainId)
    message.success(v ? '已开启：添加用户 / JIT 预配必须提供邮箱' : '已关闭：邮箱变为可选')
    void identity.run()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '保存失败')
  } finally {
    savingIdentity.value = false
  }
}

async function createWebhook() {
  savingWebhook.value = true
  try {
    await ensureLogWebhook(props.cfgId)
    message.success('回调地址已生成')
    void webhook.run()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '生成失败')
  } finally {
    savingWebhook.value = false
  }
}

async function removeWebhook() {
  savingWebhook.value = true
  try {
    await revokeLogWebhook(props.cfgId)
    message.success('回调地址已撤销，旧地址随即失效')
    void webhook.run()
  } catch (e) {
    message.error(e instanceof Error ? e.message : '撤销失败')
  } finally {
    savingWebhook.value = false
  }
}

async function copyWebhookPath() {
  const path = webhook.data.value?.webhook?.path
  if (!path) return
  try {
    await navigator.clipboard.writeText(path)
    message.success('已复制回调路径，请以面板公网域名拼接完整 URL')
  } catch {
    message.error('复制失败，请手动选择文本复制')
  }
}

function policyDesc(p: PasswordPolicy): string {
  const parts = [`最短 ${p.minLength ?? 0} 位`]
  parts.push(p.passwordExpiresAfter ? `${p.passwordExpiresAfter} 天过期` : '永不过期')
  parts.push(
    p.numPasswordsInHistory ? `不可复用最近 ${p.numPasswordsInHistory} 个密码` : '可复用以前的密码',
  )
  return `${p.passwordStrength} · ${parts.join(' · ')}`
}
</script>

<template>
  <div class="flex flex-col gap-4">
    <!-- 日志回传:全宽主卡,管线四节点 + 回调地址 + 事件清单 -->
    <div class="panel">
      <div
        class="flex flex-wrap 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">日志回传</div>
          <div class="mt-0.5 text-xs text-ink-3">
            OCI 审计关键事件推送到面板:Connector Hub → Notifications → 面板 webhook
          </div>
        </div>
        <div class="flex flex-wrap items-center gap-2">
          <span
            class="inline-flex items-center gap-1.5 rounded-full bg-wash px-2.5 py-[3px] text-[11.5px] text-ink-2"
          >
            <span class="h-1.5 w-1.5 rounded-full" :class="relayChip.dot" />
            {{ relayChip.label }}
          </span>
          <NPopconfirm v-if="relayBuilt" @positive-click="destroyRelay">
            <template #trigger>
              <NButton size="tiny" type="error" quaternary :loading="tearingRelay">
                销毁链路
              </NButton>
            </template>
            将删除租户内的 Connector、Policy、订阅与 Topic 并撤销回调地址,确认销毁?
          </NPopconfirm>
          <NButton
            v-else
            size="small"
            type="primary"
            :loading="settingUpRelay"
            @click="createRelay"
          >
            一键创建链路
          </NButton>
        </div>
      </div>
      <div v-if="webhook.loading.value" class="flex items-center justify-center py-10">
        <NSpin size="small" />
      </div>
      <template v-else>
        <div v-if="settingUpRelay" class="px-4.5 pt-3 text-xs text-warn">
          正在创建 Topic → 订阅(等待自动确认)→ Policy → Connector,约需 1-2 分钟,请勿离开
        </div>
        <div v-else-if="relay.error.value" class="px-4.5 pt-3 text-xs text-warn">
          {{ relay.error.value }}
        </div>
        <div
          v-else-if="relayBuilt"
          class="grid grid-cols-4 gap-2.5 px-4.5 pt-3.5 max-md:grid-cols-2"
        >
          <div
            v-for="row in relayRows"
            :key="row.label"
            class="relative rounded-lg bg-wash px-3 py-2.5 after:absolute after:top-1/2 after:-right-[9px] after:-translate-y-1/2 after:text-xs after:text-ink-3 after:content-['→'] last:after:hidden max-md:after:hidden"
          >
            <div class="text-[11px] tracking-wider text-ink-3 uppercase">{{ row.label }}</div>
            <div class="mt-1 flex items-center gap-1.5 text-[12.5px] font-semibold">
              <span class="h-[7px] w-[7px] flex-none rounded-full" :class="relayDotClass(row.res)" />
              {{ row.res.id ? row.res.state || '存在' : '未创建' }}
            </div>
          </div>
        </div>
        <div v-else class="px-4.5 pt-3.5 text-xs leading-relaxed text-ink-3">
          在租户内自动创建 Topic、CUSTOM_HTTPS 订阅、IAM Policy 与 Service Connector(_Audit
          审计日志,含子区间),订阅确认由面板自动完成,全程约 1-2 分钟
        </div>

        <div
          v-if="webhook.data.value?.exists"
          class="mx-4.5 mt-3 flex items-center gap-2.5 rounded-md bg-wash px-3 py-2"
        >
          <span class="flex-none text-xs text-ink-3">回调地址</span>
          <span class="mono min-w-0 flex-1 text-[12px] break-all text-ink-2">
            {{ webhook.data.value.webhook?.path }}
          </span>
          <NButton size="tiny" quaternary class="flex-none" @click="copyWebhookPath">复制</NButton>
          <NPopconfirm @positive-click="removeWebhook">
            <template #trigger>
              <NButton size="tiny" type="error" quaternary class="flex-none" :loading="savingWebhook">
                撤销
              </NButton>
            </template>
            撤销后旧回调地址立即 404,OCI 侧订阅将投递失败,确认撤销?
          </NPopconfirm>
        </div>
        <div v-else class="mx-4.5 mt-3 flex flex-wrap items-center gap-2.5 rounded-md bg-wash px-3 py-2">
          <span class="min-w-0 flex-1 text-xs text-ink-3">
            推荐直接「一键创建链路」;仅需回调地址手动配置 OCI 侧时,可单独生成
          </span>
          <NButton size="tiny" :loading="savingWebhook" @click="createWebhook">
            生成回调地址
          </NButton>
        </div>

        <div class="px-4.5 pt-3.5 pb-4">
          <div class="text-xs font-semibold text-ink-2">
            回传事件清单(OCI 侧 Log Filter 收窄,仅以下关键事件回传,不产生噪声流量)
          </div>
          <div class="mt-2 flex flex-wrap gap-1.5">
            <span
              v-for="ev in relay.data.value?.events ?? []"
              :key="ev"
              class="mono rounded bg-wash px-1.5 py-0.5 text-[11px] text-ink-2"
            >
              {{ ev }}
            </span>
          </div>
        </div>
      </template>
      <FootNote>
        关键事件入库后另经「通知管理 → 云端事件」按类推送,可在设置中关闭;回调地址内含随机凭据,
        请勿外传,复制路径后以面板公网域名拼接完整 URL
      </FootNote>
    </div>

    <div class="grid grid-cols-2 items-start gap-4 max-lg:grid-cols-1">
      <!-- 密码策略与身份 -->
      <div class="panel">
        <div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
          <div class="text-sm font-semibold">密码策略与身份</div>
          <div class="text-[12.5px] text-ink-3">Default 身份域 · IAM 规则</div>
        </div>
        <div v-if="policies.loading.value || identity.loading.value" class="flex items-center justify-center py-10">
          <NSpin size="small" />
        </div>
        <div v-else class="px-4.5 py-1.5">
          <div
            v-for="p in policies.data.value ?? []"
            :key="p.id"
            class="flex items-center gap-2.5 border-b border-line-soft py-2.5"
          >
            <div class="min-w-0 flex-1">
              <div class="flex items-center gap-2">
                <span class="mono text-[12.5px] text-ink">{{ p.name }}</span>
                <StatusBadge
                  :kind="p.passwordStrength === 'Custom' ? 'snatch' : 'check'"
                  :label="p.passwordStrength === 'Custom' ? 'Custom' : '内置'"
                  :dot="false"
                />
              </div>
              <div class="mt-0.5 text-xs text-ink-3">
                {{ policyDesc(p) }}
              </div>
            </div>
            <NButton
              v-if="p.passwordStrength === 'Custom'"
              size="tiny"
              quaternary
              @click="openPolicyEdit(p)"
            >
              编辑
            </NButton>
            <span v-else class="text-[12.5px] text-ink-3">只读</span>
          </div>
          <div class="flex items-center gap-3 py-2.5">
            <div class="min-w-0 flex-1">
              <div class="text-[13px] font-medium">用户需要提供主电子邮件地址</div>
              <div class="mt-0.5 text-xs text-ink-3">
                开启后「添加用户」必须填写邮箱,JIT 预配必须映射邮箱属性
              </div>
            </div>
            <NSwitch
              :value="identity.data.value?.primaryEmailRequired ?? false"
              :loading="savingIdentity"
              size="small"
              @update:value="toggleEmailRequired"
            />
          </div>
        </div>
        <FootNote>
          仅 Custom 强度的策略可修改,Simple / Standard 为 Oracle
          只读内置策略;关闭邮箱强制后找回密码与欢迎邮件不可用
        </FootNote>
      </div>

      <!-- 通知收件人 -->
      <div class="panel">
        <div
          class="flex flex-wrap 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">通知收件人</div>
            <div class="mt-0.5 text-xs text-ink-3">
              域通知 test mode:开启后全部通知只发给指定收件人
            </div>
          </div>
          <span
            class="inline-flex items-center gap-1.5 rounded-full bg-wash px-2.5 py-[3px] text-[11.5px] text-ink-2"
          >
            <span
              class="h-1.5 w-1.5 rounded-full"
              :class="recipients.data.value?.testModeEnabled ? 'bg-ok' : 'bg-ink-3/50'"
            />
            test mode {{ recipients.data.value?.testModeEnabled ? '已开启' : '未开启' }}
          </span>
        </div>
        <div v-if="recipients.loading.value" class="flex items-center justify-center py-10">
          <NSpin size="small" />
        </div>
        <div v-else class="flex flex-col gap-3 px-4.5 py-3.5">
          <NDynamicTags v-model:value="emails" />
          <div class="flex items-center gap-2">
            <NButton size="small" type="primary" :loading="saving" @click="saveRecipients">
              保存
            </NButton>
            <span class="text-xs text-ink-3">传空列表将关闭 test mode，恢复域默认发送</span>
          </div>
        </div>
        <FootNote>收件人为本租户级配置;适合把告警邮件集中到运维邮箱</FootNote>
      </div>
    </div>

    <PolicyEditModal
      v-model:show="showPolicyEdit"
      :cfg-id="cfgId"
      :domain-id="domainId"
      :policy="editingPolicy"
      @updated="policies.run()"
    />
  </div>
</template>
