<script lang="ts" setup>
import type { VbenFormSchema } from '@vben/common-ui'
import type { Recordable } from '@vben/types'

import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue'
import { useRoute, useRouter } from 'vue-router'

import { AuthenticationLogin, SliderCaptcha, z } from '@vben/common-ui'
import { $t } from '@vben/locales'
import { SYS_INFO } from '@vben/preferences'
import { resetAllStores } from '@vben/stores'

import AuthApi from '#/api/core/auth'
import { useAuthStore } from '#/store'
import hud from '#/utils/hud'

defineOptions({ name: 'Login' })

const authStore = useAuthStore()
const route = useRoute()
const router = useRouter()
const loginType = ref<'account' | 'wechat'>('wechat')
const wechatQrLoading = ref(false)
const wechatAuthUrl = ref('')
const wechatCallbackLoading = ref(false)
const bindVisible = ref(false)
const bindOpenid = ref('')
const bindLoading = ref(false)
const bindForm = ref({
  account: '',
  password: '',
})
const WECHAT_CALLBACK_MESSAGE = 'wechat-login-callback'

const loginTypeOptions = [
  {
    label: '微信登录',
    value: 'wechat',
  },
  {
    label: '帐号密码登录',
    value: 'account',
  },
] as const

// const MOCK_USER_OPTIONS: BasicOption[] = [
//   {
//     label: 'Super',
//     value: 'vben',
//   },
//   {
//     label: 'Admin',
//     value: 'admin',
//   },
//   {
//     label: 'User',
//     value: 'jack',
//   },
// ]

const formSchema = computed((): VbenFormSchema[] => {
  return [
    // {
    //   component: 'VbenSelect',
    //   // componentProps(_values, form) {
    //   //   return {
    //   //     'onUpdate:modelValue': (value: string) => {
    //   //       const findItem = MOCK_USER_OPTIONS.find(
    //   //         (item) => item.value === value,
    //   //       );
    //   //       if (findItem) {
    //   //         form.setValues({
    //   //           password: '123456',
    //   //           username: findItem.label,
    //   //         });
    //   //       }
    //   //     },
    //   //     options: MOCK_USER_OPTIONS,
    //   //     placeholder: $t('authentication.selectAccount'),
    //   //   };
    //   // },
    //   componentProps: {
    //     options: MOCK_USER_OPTIONS,
    //     placeholder: $t('authentication.selectAccount'),
    //   },
    //   fieldName: 'selectAccount',
    //   label: $t('authentication.selectAccount'),
    //   rules: z
    //     .string()
    //     .min(1, { message: $t('authentication.selectAccount') })
    //     .optional()
    //     .default('vben'),
    // },
    {
      component: 'VbenInput',
      componentProps: {
        placeholder: $t('authentication.usernameTip'),
      },
      // dependencies: {
      //   trigger(values, form) {
      //     if (values.selectAccount) {
      //       const findUser = MOCK_USER_OPTIONS.find(
      //         (item) => item.value === values.selectAccount,
      //       )
      //       if (findUser) {
      //         form.setValues({
      //           password: '123456',
      //           username: findUser.value,
      //         })
      //       }
      //     }
      //   },
      // triggerFields: ['selectAccount'],
      // },
      fieldName: 'username',
      label: $t('authentication.username'),
      rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
    },
    {
      component: 'VbenInputPassword',
      componentProps: {
        placeholder: $t('authentication.password'),
      },
      fieldName: 'password',
      label: $t('authentication.password'),
      rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
    },
    // {
    //   component: markRaw(SliderCaptcha),
    //   fieldName: 'captcha',
    //   rules: z.boolean().refine((value) => value, {
    //     message: $t('authentication.verifyRequiredTip'),
    //   }),
    // },
  ]
})

const loginRef =
  useTemplateRef<InstanceType<typeof AuthenticationLogin>>('loginRef')

function normalizeWechatAuthResult(data: any) {
  if (typeof data === 'string') {
    return {
      authUrl: data,
      state: new URL(data, globalThis.location.origin).searchParams.get(
        'state',
      ),
    }
  }

  const authUrl =
    data?.authUrl ?? data?.url ?? data?.authorizeUrl ?? data?.redirectUrl
  return {
    authUrl,
    state:
      data?.state ??
      (authUrl
        ? new URL(authUrl, globalThis.location.origin).searchParams.get('state')
        : ''),
  }
}

function getFirstQueryValue(value: unknown) {
  const result = Array.isArray(value) ? value[0] : value
  return typeof result === 'string' ? result : ''
}

function decodeRepeatedly(value: string) {
  let result = value
  for (let index = 0; index < 3; index += 1) {
    try {
      const decoded = decodeURIComponent(result)
      if (decoded === result) break
      result = decoded
    } catch {
      break
    }
  }
  return result
}

function getWechatCallbackParams() {
  const code = getFirstQueryValue(route.query.code)
  const state = getFirstQueryValue(route.query.state)
  if (code && state) {
    return { code, state }
  }

  const redirect = getFirstQueryValue(route.query.redirect)
  if (!redirect) {
    return null
  }

  const decodedRedirect = decodeRepeatedly(redirect)
  const redirectUrl = new URL(decodedRedirect, globalThis.location.origin)
  const redirectCode = redirectUrl.searchParams.get('code')
  const redirectState = redirectUrl.searchParams.get('state')

  return redirectCode && redirectState
    ? { code: redirectCode, state: redirectState }
    : null
}

function hasWechatCallbackCode() {
  const code = getFirstQueryValue(route.query.code)
  if (code) return true

  const redirect = getFirstQueryValue(route.query.redirect)
  if (!redirect) return false

  try {
    const decodedRedirect = decodeRepeatedly(redirect)
    const redirectUrl = new URL(decodedRedirect, globalThis.location.origin)
    return Boolean(redirectUrl.searchParams.get('code'))
  } catch {
    return false
  }
}

function postWechatCallbackToParent() {
  const callbackParams = getWechatCallbackParams()
  if (!callbackParams) return false
  if (globalThis.window.self === globalThis.window.top) return false

  globalThis.window.parent.postMessage(
    {
      params: callbackParams,
      type: WECHAT_CALLBACK_MESSAGE,
    },
    globalThis.location.origin,
  )
  return true
}

function clearWechatCallbackQuery() {
  const code = getFirstQueryValue(route.query.code)
  const state = getFirstQueryValue(route.query.state)
  const redirectRaw = getFirstQueryValue(route.query.redirect)

  // 仅当 redirect 里嵌套了 code/state 时才把它一起清掉，
  // 否则保留原本的跳转目标，避免误删用户的回跳路径。
  let redirectHasCallback = false
  if (redirectRaw) {
    try {
      const redirectUrl = new URL(
        decodeRepeatedly(redirectRaw),
        globalThis.location.origin,
      )
      redirectHasCallback = Boolean(
        redirectUrl.searchParams.get('code') &&
          redirectUrl.searchParams.get('state'),
      )
    } catch {
      redirectHasCallback = false
    }
  }

  if (!code && !state && !redirectHasCallback) return

  const newQuery: Record<string, any> = { ...route.query }
  delete newQuery.code
  delete newQuery.state
  if (redirectHasCallback) {
    delete newQuery.redirect
  }

  router.replace({
    path: route.path,
    query: newQuery,
  })
}

async function loadWechatAuthUrl() {
  wechatQrLoading.value = true
  wechatAuthUrl.value = ''
  try {
    const result = normalizeWechatAuthResult(await AuthApi.getWechatAuthUrl())
    if (!result.authUrl) {
      hud.error('获取微信授权地址失败')
      return
    }

    wechatAuthUrl.value = result.authUrl
  } finally {
    wechatQrLoading.value = false
  }
}

async function handleWechatCallback(
  callbackParams = getWechatCallbackParams(),
) {
  if (!callbackParams) {
    return false
  }
  if (wechatCallbackLoading.value) {
    return true
  }

  loginType.value = 'wechat'
  wechatAuthUrl.value = ''
  wechatCallbackLoading.value = true
  try {
    const result = await authStore.authWechatLogin(
      callbackParams,
      redirectToHomeAfterWechatLogin,
    )
    if (result.bindRequired) {
      const openid = result.openid
      if (!openid) {
        hud.error('微信登录返回缺少 openid，无法绑定后台账号')
        clearWechatCallbackQuery()
        return true
      }

      bindOpenid.value = openid
      bindVisible.value = true
      // 注意：这里不调用 clearWechatCallbackQuery。
      // 父级 AuthenticationFormView 的 RouterView 使用了
      // `:key="route.fullPath"`，路由 query 一旦变化就会导致 Login.vue
      // 整个被卸载并重新挂载，刚弹出的绑定弹框也会被一起销毁。
      // 绑定成功后会在 completeLogin 里跳转到首页，URL 自然就脱离了登录页，
      // code/state 不会再被复用。
    } else {
      // 登录成功且无需绑定，回收回调参数，避免刷新时重复使用
      clearWechatCallbackQuery()
    }
  } catch (error: any) {
    hud.error(error?.message || '微信登录失败')
    // 出错时清掉 code/state，让用户可以重新扫码
    clearWechatCallbackQuery()
  } finally {
    wechatCallbackLoading.value = false
  }

  return true
}

function redirectToHomeAfterWechatLogin() {
  // 微信扫码场景下，URL 上常常带着指向 /wechat/callback 的 redirect query
  // （来自微信 OAuth 的 redirect_uri）。如果走默认的 router.push(HOME_PATH)，
  // setupAccessGuard 会读取 from.query.redirect，把用户带到 /wechat/callback。
  // 这里直接做一次硬跳转回站点根路径，由首页路由再走正常的鉴权流程。
  globalThis.location.replace('/')
}

function handleWechatCallbackMessage(event: MessageEvent) {
  if (event.origin !== globalThis.location.origin) return

  const data = event.data
  if (data?.type !== WECHAT_CALLBACK_MESSAGE) return

  const code = data?.params?.code
  const state = data?.params?.state
  if (typeof code !== 'string' || typeof state !== 'string') return

  handleWechatCallback({ code, state })
}

async function submitWechatBind() {
  if (!bindForm.value.account) {
    hud.error('请输入账号')
    return
  }
  if (!bindForm.value.password) {
    hud.error('请输入密码')
    return
  }

  bindLoading.value = true
  try {
    await authStore.authWechatBind(
      {
        account: bindForm.value.account,
        openid: bindOpenid.value,
        password: bindForm.value.password,
      },
      redirectToHomeAfterWechatLogin,
    )
    bindVisible.value = false
  } catch (error: any) {
    hud.error(error?.message || '微信绑定失败')
  } finally {
    bindLoading.value = false
  }
}

async function onSubmit(params: Recordable<any>) {
  authStore.authLogin(params).catch(() => {
    // 登陆失败，刷新验证码的演示
    const formApi = loginRef.value?.getFormApi()
    // 重置验证码组件的值
    formApi?.setFieldValue('captcha', false, false)
    // 使用表单API获取验证码组件实例，并调用其resume方法来重置验证码
    formApi
      ?.getFieldComponentRef<InstanceType<typeof SliderCaptcha>>('captcha')
      ?.resume()
  })
}

onMounted(async () => {
  if (postWechatCallbackToParent()) {
    return
  }

  globalThis.window.addEventListener('message', handleWechatCallbackMessage)

  const isWechatCallbackCode = hasWechatCallbackCode()
  if (!isWechatCallbackCode) {
    resetAllStores()
  }

  const isWechatCallback = await handleWechatCallback()
  if (!isWechatCallback && !isWechatCallbackCode) {
    loadWechatAuthUrl()
  }
})

onBeforeUnmount(() => {
  globalThis.window.removeEventListener('message', handleWechatCallbackMessage)
})
</script>

<template>
  <div>
    <div class="mb-7 sm:mx-auto sm:w-full sm:max-w-md">
      <h2
        class="text-foreground mb-3 text-3xl font-bold leading-9 tracking-tight lg:text-4xl"
      >
        {{ SYS_INFO.loginTitle }}
      </h2>
      <p class="text-muted-foreground lg:text-md text-sm">
        {{ SYS_INFO.loginSubtitle }}
      </p>
    </div>

    <div class="bg-muted mb-6 grid grid-cols-2 rounded-lg p-1 text-sm">
      <button
        v-for="item in loginTypeOptions"
        :key="item.value"
        type="button"
        class="rounded-md px-3 py-2 font-medium transition"
        :class="
          loginType === item.value
            ? 'bg-background text-foreground shadow-sm'
            : 'text-muted-foreground hover:text-foreground'
        "
        @click="loginType = item.value"
      >
        {{ item.label }}
      </button>
    </div>

    <AuthenticationLogin
      v-if="loginType === 'account'"
      ref="loginRef"
      :form-schema="formSchema"
      :loading="authStore.loginLoading"
      @submit="onSubmit"
    >
      <template #title>
        <div></div>
      </template>
    </AuthenticationLogin>

    <div v-else class="flex-col-center rounded-lg p-6">
      <div
        class="bg-background flex h-[420px] w-full items-center justify-center overflow-hidden rounded-lg"
      >
        <iframe
          v-if="wechatAuthUrl"
          :src="wechatAuthUrl"
          title="微信扫码登录"
          class="h-full w-full border-0"
        ></iframe>
        <span v-else class="text-muted-foreground text-sm">
          {{ wechatQrLoading ? '正在获取微信登录二维码...' : '二维码加载失败' }}
        </span>
      </div>
      <p class="text-foreground mt-5 text-sm font-medium">请使用微信扫码登录</p>
      <p class="text-muted-foreground mt-2 text-center text-xs">
        扫码后按提示完成授权，即可进入灵犀学AI平台
      </p>
      <button
        type="button"
        class="text-primary mt-4 text-xs hover:opacity-80"
        :disabled="wechatQrLoading"
        @click="loadWechatAuthUrl"
      >
        刷新二维码
      </button>
      <p
        v-if="wechatCallbackLoading"
        class="text-muted-foreground mt-4 text-xs"
      >
        正在处理微信授权结果...
      </p>
    </div>

    <el-dialog
      v-model="bindVisible"
      title="绑定后台账号"
      width="420px"
      align-center
      append-to-body
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      destroy-on-close
    >
      <div class="space-y-4">
        <p class="text-muted-foreground text-sm">
          该微信尚未绑定后台账号，请输入账号密码完成绑定。
        </p>
        <div>
          <label class="text-foreground mb-2 block text-sm">账号</label>
          <el-input
            v-model="bindForm.account"
            clearable
            placeholder="请输入账号"
            @keyup.enter="submitWechatBind"
          />
        </div>
        <div>
          <label class="text-foreground mb-2 block text-sm">密码</label>
          <el-input
            v-model="bindForm.password"
            clearable
            placeholder="请输入密码"
            show-password
            type="password"
            @keyup.enter="submitWechatBind"
          />
        </div>
        <el-button
          class="w-full"
          type="primary"
          :loading="bindLoading"
          @click="submitWechatBind"
        >
          绑定并登录
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
