<script setup lang="ts">
import { NButton, NInput, NSpin } from 'naive-ui'
import { onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'

import { getOauthAuthorizeUrl, getOauthProviders, login } from '@/api/auth'
import { ApiError } from '@/api/request'
import AppLogo from '@/components/AppLogo.vue'
import FullPageBackdrop from '@/components/FullPageBackdrop.vue'
import OauthProviderIcon from '@/components/OauthProviderIcon.vue'
import { useThemeRipple } from '@/composables/useThemeRipple'
import { useAuthStore } from '@/stores/auth'
import type { OauthProviderInfo } from '@/types/api'

const auth = useAuthStore()
const route = useRoute()
const router = useRouter()
// 点击 logo 切换明暗主题,新主题自 logo 向外扩散
const { toggleDarkFrom } = useThemeRipple()

const username = ref('admin')
const password = ref('')
const totpCode = ref('')
// 密码通过但账号启用了两步验证:展示验证码输入
const totpRequired = ref(false)
const loading = ref(false)
const errorMsg = ref('')

// 可登录的外部方式(已禁用的后端不返回);未配置时不显示分隔区
const providers = ref<OauthProviderInfo[]>([])
// 密码登录已禁用(设置里开启且存在可用外部身份):隐藏用户名密码表单
const passwordLoginDisabled = ref(false)
// 登录方式加载中:providers 返回前不渲染表单,避免「先见密码表单再消失」的闪烁与误提交
const bootLoading = ref(true)

// 左侧品牌区功能徽章:各自不同周期/幅度/相位上下浮动(不规律观感)
const featureChips = [
  { label: '实例测活', dot: 'var(--color-ok)', style: '--bd:5.7s;--bdel:-1.3s;--bamp:-7px' },
  { label: '自动抢机', dot: 'var(--color-accent)', style: '--bd:7.4s;--bdel:-4.1s;--bamp:-5px' },
  { label: 'AI 网关', dot: 'var(--color-info)', style: '--bd:6.2s;--bdel:-2.6s;--bamp:-9px' },
  { label: '日志回传', dot: 'var(--color-warn)', style: '--bd:8.3s;--bdel:-0.8s;--bamp:-6px' },
]

onMounted(async () => {
  consumeOauthCallback()
  try {
    const r = await getOauthProviders()
    providers.value = r.providers
    passwordLoginDisabled.value = r.passwordLoginDisabled && r.providers.length > 0
  } catch {
    /* 登录页容忍 provider 列表加载失败 */
  } finally {
    bootLoading.value = false
  }
})

/** 处理 OAuth 回跳:fragment 中的 token 直接建会话,query 中的错误展示后清除 */
function consumeOauthCallback() {
  const hash = window.location.hash
  if (hash.startsWith('#oauthToken=')) {
    const token = decodeURIComponent(hash.slice('#oauthToken='.length))
    history.replaceState(null, '', window.location.pathname)
    // OAuth 登录未返回过期时间,按后端 JWT 24h 计
    auth.setSession(token, new Date(Date.now() + 24 * 3600e3).toISOString())
    void router.push({ name: 'overview' })
    return
  }
  const err = route.query.oauthError
  if (typeof err === 'string' && err) {
    errorMsg.value = err
    void router.replace({ query: {} })
  }
}

async function submit() {
  if (!username.value || !password.value) {
    errorMsg.value = '请输入用户名和密码'
    return
  }
  if (totpRequired.value && !totpCode.value) {
    errorMsg.value = '请输入两步验证码'
    return
  }
  loading.value = true
  errorMsg.value = ''
  try {
    const resp = await login({
      username: username.value,
      password: password.value,
      ...(totpCode.value ? { totpCode: totpCode.value } : {}),
    })
    auth.setSession(resp.token, resp.expiresAt)
    const redirect = route.query.redirect
    await router.push(typeof redirect === 'string' ? redirect : { name: 'overview' })
  } catch (e) {
    if (e instanceof ApiError && e.status === 428) {
      totpRequired.value = true
      return
    }
    errorMsg.value = e instanceof Error ? e.message : '登录失败'
  } finally {
    loading.value = false
  }
}

async function oauthLogin(provider: string) {
  loading.value = true
  errorMsg.value = ''
  try {
    const { url } = await getOauthAuthorizeUrl(provider, 'login')
    window.location.href = url
  } catch (e) {
    errorMsg.value = e instanceof Error ? e.message : '跳转失败'
    loading.value = false
  }
}
</script>

<template>
  <div class="relative flex min-h-screen items-center overflow-hidden">
    <FullPageBackdrop tint="accent" rings="none" />

    <div
      class="relative z-10 mx-auto flex w-full max-w-[1180px] items-center justify-between gap-12 px-12 max-lg:flex-col max-lg:justify-center max-lg:gap-9 max-lg:px-6 max-lg:py-10"
    >
      <!-- 品牌区(双轨道环以品牌内容为中心,与设计稿同心) -->
      <div class="relative flex max-w-[480px] flex-col gap-7 max-lg:items-center max-lg:gap-5 max-lg:text-center">
        <div class="pointer-events-none absolute inset-0 max-lg:hidden" aria-hidden="true">
          <div class="fpb-ring fpb-spin" style="width: 580px; height: 580px; left: calc(50% - 290px); top: calc(50% - 290px)"></div>
          <div class="fpb-ring fpb-ring-dash" style="width: 430px; height: 430px; left: calc(50% - 215px); top: calc(50% - 215px)"></div>
        </div>
        <div class="flex items-center gap-3">
          <button
            type="button"
            class="floaty logo-btn flex-none"
            title="切换明暗主题"
            aria-label="切换明暗主题"
            @click="toggleDarkFrom"
          >
            <AppLogo :size="58" />
          </button>
          <span>
            <span class="block font-serif text-2xl font-semibold">OCI Portal</span>
            <span class="mt-px block text-[12.5px] text-ink-3">自托管 · Self-hosted</span>
          </span>
        </div>
        <div class="font-serif text-[46px] leading-[1.24] font-semibold max-lg:text-[30px]">
          批量管理你的<br />Oracle Cloud 租户
        </div>
        <div class="flex max-w-[420px] flex-wrap gap-2 max-lg:justify-center">
          <span v-for="c in featureChips" :key="c.label" class="chip" :style="c.style">
            <span class="dot" :style="{ background: c.dot }"></span>{{ c.label }}
          </span>
        </div>
      </div>

      <!-- 登录卡 -->
      <div class="w-[392px] max-w-full flex-none">
        <div class="rounded-[14px] border border-line bg-card px-[30px] pt-[30px] pb-7 shadow-[0_18px_50px_rgba(20,20,19,.10),0_2px_8px_rgba(20,20,19,.05)]">
          <div class="font-serif text-[19px] font-semibold">欢迎回来</div>

          <div v-if="bootLoading" class="flex items-center justify-center py-14">
            <NSpin size="small" />
          </div>

          <template v-else>
          <div
            v-if="errorMsg"
            class="mt-4 flex items-start gap-2 rounded-md border border-err/30 bg-err/10 px-3 py-2 text-[12.5px] text-err"
            role="alert"
          >
            {{ errorMsg }}
          </div>

          <form v-if="!passwordLoginDisabled" class="mt-5 flex flex-col gap-4" @submit.prevent="submit">
            <label class="flex flex-col gap-1.5">
              <span class="text-[13px] font-medium text-ink-2">用户名</span>
              <NInput v-model:value="username" placeholder="admin" :disabled="loading" />
            </label>

            <label class="flex flex-col gap-1.5">
              <span class="text-[13px] font-medium text-ink-2">密码</span>
              <NInput
                v-model:value="password"
                type="password"
                show-password-on="click"
                placeholder="请输入密码"
                :disabled="loading"
                @keyup.enter="submit"
              />
            </label>

            <label v-if="totpRequired" class="flex flex-col gap-1.5">
              <span class="text-[13px] font-medium text-ink-2">两步验证码</span>
              <NInput
                v-model:value="totpCode"
                placeholder="验证器 App 中的 6 位数字"
                :maxlength="6"
                :disabled="loading"
                @keyup.enter="submit"
              />
              <span class="text-xs text-ink-3">该账号已启用两步验证,请输入动态验证码完成登录</span>
            </label>

            <NButton type="primary" attr-type="submit" size="large" block :loading="loading">
              {{ loading ? '验证中…' : '登 录' }}
            </NButton>
          </form>
          <div v-else class="mt-1.5 text-[12.5px] text-ink-3">密码登录已禁用,请使用外部身份继续</div>

          <template v-if="providers.length">
            <div v-if="!passwordLoginDisabled" class="mt-5 flex items-center gap-2.5 text-[11.5px] text-ink-3">
              <span class="h-px flex-1 bg-line-soft"></span>
              或
              <span class="h-px flex-1 bg-line-soft"></span>
            </div>
            <div class="flex" :class="passwordLoginDisabled ? 'mt-6 flex-col gap-2.5' : 'mt-4 gap-2'">
              <NButton
                v-for="p in providers"
                :key="p.provider"
                :block="passwordLoginDisabled"
                :class="passwordLoginDisabled ? '' : 'min-w-0 flex-1'"
                :size="passwordLoginDisabled ? 'large' : 'medium'"
                :disabled="loading"
                @click="oauthLogin(p.provider)"
              >
                <template #icon><OauthProviderIcon :provider="p.provider" /></template>
                {{ passwordLoginDisabled ? `使用 ${p.displayName} 登录` : p.displayName }}
              </NButton>
            </div>
            <div v-if="passwordLoginDisabled" class="mt-5 flex items-center justify-center gap-1.5 text-[11.5px] text-ink-3">
              <svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
                <rect x="5" y="11" width="14" height="9" rx="2"></rect>
                <path d="M8 11V7a4 4 0 0 1 8 0v4"></path>
              </svg>
              由管理员在 设置 · 账号安全 中管理
            </div>
          </template>
          </template>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--color-line);
  border-radius: 999px;
  background: var(--color-card);
  padding: 7px 14px;
  font-size: 12px;
  color: var(--color-ink-2);
  box-shadow: 0 2px 8px rgba(20, 20, 19, 0.07);
}
.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
}
/* logo 即主题开关:去按钮底色,悬停轻微放大提示可点 */
.logo-btn {
  cursor: pointer;
  border: none;
  background: none;
  padding: 0;
  line-height: 0;
  transition: scale 0.25s ease;
}
.logo-btn:hover {
  scale: 1.06;
}
.logo-btn:active {
  scale: 0.96;
}
@media (prefers-reduced-motion: no-preference) {
  .floaty {
    animation: login-fl 7s ease-in-out infinite;
  }
  .chip {
    animation: login-bob var(--bd, 6.5s) ease-in-out var(--bdel, 0s) infinite;
  }
  @keyframes login-fl {
    0%,
    100% {
      transform: translateY(0);
    }
    50% {
      transform: translateY(-10px);
    }
  }
  @keyframes login-bob {
    0%,
    100% {
      transform: translateY(0);
    }
    32% {
      transform: translateY(var(--bamp, -6px));
    }
    68% {
      transform: translateY(calc(var(--bamp, -6px) * -0.4));
    }
  }
}
</style>
