<template>
    <div class="bind-wx flex flex-col">
        <div class="flex-1 flex items-center justify-center">
            <div class="bind-wx-card bg-body rounded-md px-10 py-10 w-[480px]">
                <div class="text-center text-2xl font-medium mb-2">绑定企业微信</div>
                <div class="text-center text-gray-500 text-sm mb-8">
                    根据安全策略，需绑定企业微信账号后方可使用管理后台
                </div>

                <div v-if="wxWorkAutoAuth" class="text-center py-10">
                    <el-icon class="is-loading mb-4" :size="40" color="var(--el-color-primary)">
                        <Loading />
                    </el-icon>
                    <div class="text-gray-500">企业微信授权中...</div>
                </div>

                <template v-else>
                    <div v-if="wxWorkLoading" class="text-center py-10">
                        <el-icon class="is-loading" :size="32" color="var(--el-color-primary)">
                            <Loading />
                        </el-icon>
                        <div class="mt-2 text-gray-400 text-sm">加载扫码...</div>
                    </div>
                    <div v-else id="wxwork_bind_qrcode_container" class="wxwork-qrcode mx-auto"></div>
                    <div class="text-center text-sm text-gray-400 mt-4">
                        请使用企业微信扫描二维码完成绑定
                    </div>
                </template>

                <div class="mt-8 flex justify-center gap-4">
                    <el-button @click="handleLogout">退出登录</el-button>
                </div>
            </div>
        </div>
        <layout-footer />
    </div>
</template>

<script lang="ts" setup>
import { Loading } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { nextTick, onMounted, onUnmounted, ref } from 'vue'

import LayoutFooter from '@/layout/components/footer.vue'
import { bindWorkWechat, getWorkWechatConfig } from '@/api/user'
import { PageEnum } from '@/enums/pageEnum'
import useUserStore from '@/stores/modules/user'
import { getToken } from '@/utils/auth'
import { attachWecomOAuthMessageCapture } from '@/utils/wecomOauthPostMessage'

/** 与 user/setting.vue 一致，便于可信域名只配置一种回调 URL */
const WX_BIND_STATE = 'bind_wxwork'

/** 企微 JSSDK 扫码后可能通过 postMessage 触发整页跳转；若在 await 前 replaceState 清掉 query，二次进入 onMounted 会拿不到 code，误走扫码初始化并取消 getConfig */
const WX_BIND_OAUTH_LOCK = 'like_admin_wx_bind_oauth'

const router = useRouter()
const userStore = useUserStore()

const wxWorkLoading = ref(false)
const wxWorkAutoAuth = ref(false)
const wxConfig = ref<{ corp_id: string; agent_id: string }>({ corp_id: '', agent_id: '' })

const isInWxWork = () => /wxwork/i.test(navigator.userAgent)

const getRedirectUri = () => window.location.origin + window.location.pathname + '?bind_wxwork=1'

/** 从 search 与 hash 中解析 OAuth 参数（少数网关/回跳会把参数放在 hash 后） */
function getOAuthQuery(): URLSearchParams {
    const merged = new URLSearchParams(window.location.search)
    const hash = window.location.hash || ''
    const hashQuery = hash.includes('?') ? hash.split('?').slice(1).join('?') : ''
    if (hashQuery) {
        new URLSearchParams(hashQuery).forEach((v, k) => {
            if (!merged.has(k)) merged.set(k, v)
        })
    }
    return merged
}

const handleLogout = () => {
    userStore.logout()
}

const afterBindSuccess = async () => {
    try {
        await userStore.getUserInfo()
    } catch {
        // 忽略：后续路由仍会拉取
    }
    if (userStore.isPaw === 0) {
        router.replace('/change-password')
        return
    }
    router.replace(PageEnum.INDEX)
}

const submitBindCode = async (code: string) => {
    wxWorkAutoAuth.value = true
    try {
        await bindWorkWechat({ code })
        // 仅成功后再清 query；放 finally 会在失败时也清掉，用户一刷新 code 就没了、也无法 F5 重试
        window.history.replaceState({}, '', window.location.pathname)
        await afterBindSuccess()
    } catch (e: any) {
        console.error(e)
        ElMessage.error(e?.msg || e?.message || '绑定失败')
        wxWorkAutoAuth.value = false
    }
}

let detachWecomOAuthCapture: (() => void) | null = null

const renderQr = async () => {
    if (!wxConfig.value.corp_id) return
    wxWorkLoading.value = true
    if (!(window as any).WwLogin) {
        await new Promise<void>((resolve, reject) => {
            const script = document.createElement('script')
            script.src = 'https://wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js'
            script.onload = () => resolve()
            script.onerror = () => reject(new Error('加载企业微信 JS 失败'))
            document.head.appendChild(script)
        })
    }
    await nextTick()
    wxWorkLoading.value = false
    await nextTick()
    const redirectUri = encodeURIComponent(getRedirectUri())
    new (window as any).WwLogin({
        id: 'wxwork_bind_qrcode_container',
        appid: wxConfig.value.corp_id,
        agentid: wxConfig.value.agent_id,
        redirect_uri: redirectUri,
        lang: 'zh',
        state: WX_BIND_STATE,
        href: '',
        self_redirect: false
    })
}

onMounted(async () => {
    // 与 axios 拦截器竞态：首屏若有请求先返回 10，可能在 onMounted 执行前就 router.replace 清掉 query；同步快照尽量早读
    let oauthCodeSnapshot = ''
    try {
        oauthCodeSnapshot = (new URLSearchParams(window.location.search).get('code') || '').trim()
    } catch {
        oauthCodeSnapshot = ''
    }

    if (!getToken()) {
        ElMessage.error('请先登录')
        router.push(PageEnum.LOGIN)
        return
    }

    const urlParams = getOAuthQuery()
    const wxCode = oauthCodeSnapshot || (urlParams.get('code') || '').trim()
    const wxStateRaw = urlParams.get('state')
    const wxState = wxStateRaw != null ? String(wxStateRaw).trim() : ''
    const legacyStateOk = wxState === 'admin_bind_wx'
    const stateOk =
        wxState === WX_BIND_STATE ||
        legacyStateOk ||
        wxState.toLowerCase() === WX_BIND_STATE.toLowerCase()
    const bindFlagOk = urlParams.get('bind_wxwork') === '1'
    // 扫码组件回跳有时只带 code，不带 state / bind_wxwork；若仍严格校验则刷新后永远进不了绑定逻辑
    const bindPageImplicitOk =
        !!wxCode && wxState === '' && router.currentRoute.value.path === '/bind-work-wechat'

    if (wxCode && (stateOk || bindFlagOk || bindPageImplicitOk)) {
        if (sessionStorage.getItem(WX_BIND_OAUTH_LOCK) === wxCode) {
            return
        }
        sessionStorage.setItem(WX_BIND_OAUTH_LOCK, wxCode)
        try {
            await submitBindCode(wxCode)
        } finally {
            sessionStorage.removeItem(WX_BIND_OAUTH_LOCK)
        }
        return
    }

    try {
        const res: any = await getWorkWechatConfig()
        if (!res?.enabled || !res.corp_id) {
            ElMessage.error('企业微信未配置，请联系管理员')
            return
        }
        wxConfig.value = { corp_id: res.corp_id, agent_id: res.agent_id }

        if (isInWxWork()) {
            wxWorkAutoAuth.value = true
            const redirectUri = encodeURIComponent(getRedirectUri())
            const authUrl =
                `https://open.weixin.qq.com/connect/oauth2/authorize` +
                `?appid=${res.corp_id}` +
                `&redirect_uri=${redirectUri}` +
                `&response_type=code` +
                `&scope=snsapi_privateinfo` +
                `&agentid=${res.agent_id}` +
                `&state=${WX_BIND_STATE}` +
                `#wechat_redirect`
            window.location.href = authUrl
            return
        }

        detachWecomOAuthCapture?.()
        detachWecomOAuthCapture = attachWecomOAuthMessageCapture({
            pathIncludes: 'bind-work-wechat',
            lockKey: WX_BIND_OAUTH_LOCK,
            wxBindState: WX_BIND_STATE,
            onCode: (code) => submitBindCode(code)
        })
        await renderQr()
    } catch (e) {
        console.error(e)
        ElMessage.error('获取企业微信配置失败')
    }
})

onUnmounted(() => {
    detachWecomOAuthCapture?.()
    detachWecomOAuthCapture = null
})
</script>

<style lang="scss" scoped>
.bind-wx {
    background-image: url('./images/login_bg.png');
    @apply min-h-screen bg-no-repeat bg-center bg-cover;
}

.wxwork-qrcode {
    width: 340px;
    height: 400px;
    overflow: hidden;
    :deep(iframe) {
        width: 340px !important;
        height: 400px !important;
        border: none;
    }
}
</style>
