<template>
    <div>
        <div class="switch-language">
            <el-dropdown size="large" :hide-timeout="50" placement="bottom-end" :hide-on-click="true">
                <Icon name="fa fa-globe" color="var(--el-text-color-secondary)" size="28" />
                <template #dropdown>
                    <el-dropdown-menu class="chang-lang">
                        <el-dropdown-item v-for="item in config.lang.langArray" :key="item.name" @click="editDefaultLang(item.name)">
                            {{ item.value }}
                        </el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
        <div @contextmenu.stop="" id="bubble" class="bubble">
            <canvas id="bubble-canvas" class="bubble-canvas"></canvas>
        </div>
        <div class="login">
            <div class="login-box">
                <div class="head">
                    <img src="~assets/login-header.png" alt="" />
                </div>
                <div class="form">
                    <img class="profile-avatar" :src="fullUrl('/static/images/avatar.png')" alt="" />
                    <div class="content">
                        <!-- 账号密码登录 -->
                        <el-form
                            v-if="state.step === 'login'"
                            @keyup.enter="onSubmitPre()"
                            ref="formRef"
                            :rules="rules"
                            size="large"
                            :model="form"
                        >
                            <el-form-item prop="username">
                                <el-input
                                    ref="usernameRef"
                                    type="text"
                                    clearable
                                    v-model="form.username"
                                    :placeholder="t('login.Please enter an account')"
                                >
                                    <template #prefix>
                                        <Icon name="fa fa-user" class="form-item-icon" size="16" color="var(--el-input-icon-color)" />
                                    </template>
                                </el-input>
                            </el-form-item>
                            <el-form-item prop="password">
                                <el-input
                                    ref="passwordRef"
                                    v-model="form.password"
                                    type="password"
                                    :placeholder="t('login.Please input a password')"
                                    show-password
                                >
                                    <template #prefix>
                                        <Icon name="fa fa-unlock-alt" class="form-item-icon" size="16" color="var(--el-input-icon-color)" />
                                    </template>
                                </el-input>
                            </el-form-item>
                            <el-checkbox v-model="form.keep" :label="t('login.Hold session')" size="default"></el-checkbox>
                            <el-form-item>
                                <el-button
                                    :loading="state.submitLoading"
                                    class="submit-button"
                                    round
                                    type="primary"
                                    size="large"
                                    @click="onSubmitPre()"
                                >
                                    {{ t('login.Sign in') }}
                                </el-button>
                            </el-form-item>
                        </el-form>

                        <!-- 谷歌验证器验证码 -->
                        <el-form
                            v-else-if="state.step === 'totp'"
                            @keyup.enter="onTotpVerify()"
                            ref="totpFormRef"
                            :rules="totpRules"
                            size="large"
                            :model="totpForm"
                        >
                            <div class="step-title">{{ t('login.Google Authenticator code') }}</div>
                            <div class="step-desc">{{ state.username }}</div>
                            <el-form-item prop="code">
                                <el-input
                                    v-model="totpForm.code"
                                    maxlength="6"
                                    :placeholder="t('login.Please enter the 6-digit code')"
                                    clearable
                                >
                                    <template #prefix>
                                        <Icon name="fa fa-shield" class="form-item-icon" size="16" color="var(--el-input-icon-color)" />
                                    </template>
                                </el-input>
                            </el-form-item>
                            <el-form-item>
                                <el-button
                                    :loading="state.submitLoading"
                                    class="submit-button"
                                    round
                                    type="primary"
                                    size="large"
                                    @click="onTotpVerify()"
                                >
                                    {{ state.submitLoading ? t('login.Verifying...') : t('login.Verify and sign in') }}
                                </el-button>
                            </el-form-item>
                            <el-button class="back-button" link type="primary" @click="resetToLogin">{{ t('login.Back to login') }}</el-button>
                        </el-form>

                        <!-- 绑定谷歌验证器 -->
                        <div v-else-if="state.step === 'bind'" class="bind-panel">
                            <div class="step-title">{{ t('login.Bind Google Authenticator') }}</div>
                            <div class="step-desc">{{ t('login.Scan QR code with Google Authenticator') }}</div>
                            <div v-loading="state.bindLoading" class="qr-wrap">
                                <img v-if="state.qrCode" :src="state.qrCode" alt="QR Code" class="qr-image" />
                            </div>
                            <div v-if="state.secret" class="secret-box">
                                <span class="secret-label">{{ t('login.Or enter secret manually') }}:</span>
                                <span class="secret-value">{{ state.secret }}</span>
                            </div>
                            <el-form @keyup.enter="onBindConfirm()" ref="bindFormRef" :rules="totpRules" size="large" :model="totpForm">
                                <el-form-item prop="code">
                                    <el-input
                                        v-model="totpForm.code"
                                        maxlength="6"
                                        :placeholder="t('login.Please enter the 6-digit code')"
                                        clearable
                                    >
                                        <template #prefix>
                                            <Icon name="fa fa-shield" class="form-item-icon" size="16" color="var(--el-input-icon-color)" />
                                        </template>
                                    </el-input>
                                </el-form-item>
                                <el-form-item>
                                    <el-button
                                        :loading="state.submitLoading"
                                        class="submit-button"
                                        round
                                        type="primary"
                                        size="large"
                                        @click="onBindConfirm()"
                                    >
                                        {{ state.submitLoading ? t('login.Binding...') : t('login.Confirm bind') }}
                                    </el-button>
                                </el-form-item>
                            </el-form>
                            <el-button class="back-button" link type="primary" @click="resetToLogin">{{ t('login.Back to login') }}</el-button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { onMounted, onBeforeUnmount, reactive, nextTick, useTemplateRef } from 'vue'
import * as pageBubble from '/@/utils/pageBubble'
import { useI18n } from 'vue-i18n'
import { editDefaultLang } from '/@/lang/index'
import { useConfig } from '/@/stores/config'
import { useAdminInfo } from '/@/stores/adminInfo'
import { login, totpVerify, totpBindInit, totpBindConfirm } from '/@/api/backend'
import { uuid } from '/@/utils/random'
import { buildValidatorData } from '/@/utils/validate'
import router from '/@/router'
import clickCaptcha from '/@/components/clickCaptcha'
import toggleDark from '/@/utils/useDark'
import { fullUrl } from '/@/utils/common'
import { adminBaseRoutePath } from '/@/router/static/adminBase'
import type { FormItemRule } from 'element-plus'
let timer: number

const config = useConfig()
const adminInfo = useAdminInfo()
toggleDark(config.layout.isDark)

const formRef = useTemplateRef('formRef')
const totpFormRef = useTemplateRef('totpFormRef')
const bindFormRef = useTemplateRef('bindFormRef')
const usernameRef = useTemplateRef('usernameRef')
const passwordRef = useTemplateRef('passwordRef')

const state = reactive({
    showCaptcha: false,
    submitLoading: false,
    bindLoading: false,
    step: 'login' as 'login' | 'totp' | 'bind',
    tempToken: '',
    username: '',
    secret: '',
    qrCode: '',
})

const form = reactive({
    username: '',
    password: '',
    keep: false,
    captchaId: uuid(),
    captchaInfo: '',
})

const totpForm = reactive({
    code: '',
})

const { t } = useI18n()

const rules = reactive({
    username: [buildValidatorData({ name: 'required', message: t('login.Please enter an account') }), buildValidatorData({ name: 'account' })],
    password: [buildValidatorData({ name: 'required', message: t('login.Please input a password') }), buildValidatorData({ name: 'password' })],
})

const totpRules: Partial<Record<string, FormItemRule[]>> = reactive({
    code: [
        buildValidatorData({ name: 'required', message: t('login.Please enter the 6-digit code') }),
        {
            validator: (_rule, val, callback) => {
                if (!/^\d{6}$/.test(val)) {
                    callback(new Error(t('login.Please enter the 6-digit code')))
                } else {
                    callback()
                }
            },
            trigger: 'blur',
        },
    ],
})

const focusInput = () => {
    if (form.username === '') {
        usernameRef.value?.focus()
    } else if (form.password === '') {
        passwordRef.value?.focus()
    }
}

const resetToLogin = () => {
    state.step = 'login'
    state.tempToken = ''
    state.secret = ''
    state.qrCode = ''
    state.username = ''
    totpForm.code = ''
    nextTick(() => focusInput())
}

const navigateAfterLogin = (userInfo: anyObj) => {
    if (!userInfo?.token) {
        return
    }
    adminInfo.dataFill(userInfo, false)
    nextTick(() => {
        router.push({ path: adminBaseRoutePath })
    })
}

onMounted(() => {
    timer = window.setTimeout(() => {
        pageBubble.init()
    }, 1000)

    login('get')
        .then((res) => {
            state.showCaptcha = res.data.captcha
            nextTick(() => focusInput())
        })
        .catch((err) => {
            console.log(err)
        })
})

onBeforeUnmount(() => {
    clearTimeout(timer)
    pageBubble.removeListeners()
})

const onSubmitPre = () => {
    formRef.value?.validate((valid) => {
        if (valid) {
            if (state.showCaptcha) {
                clickCaptcha(form.captchaId, (captchaInfo: string) => onSubmit(captchaInfo))
            } else {
                onSubmit()
            }
        }
    })
}

const onSubmit = (captchaInfo = '') => {
    state.submitLoading = true
    form.captchaInfo = captchaInfo
    login('post', form)
        .then((res) => {
            const data = res?.data
            if (data?.userInfo?.token) {
                navigateAfterLogin(data.userInfo)
                return
            }
            if (data?.type === 'need_totp' && data?.tempToken) {
                state.step = 'totp'
                state.tempToken = data.tempToken
                state.username = data.username || form.username
                totpForm.code = ''
                return
            }
            if (data?.type === 'need_bind_totp' && data?.tempToken) {
                state.step = 'bind'
                state.tempToken = data.tempToken
                state.username = data.username || form.username
                totpForm.code = ''
                loadBindInfo()
            }
        })
        .finally(() => {
            state.submitLoading = false
        })
}

const loadBindInfo = () => {
    state.bindLoading = true
    totpBindInit({ tempToken: state.tempToken })
        .then((res) => {
            state.secret = res.data.secret
            state.qrCode = res.data.qrCode
        })
        .finally(() => {
            state.bindLoading = false
        })
}

const onTotpVerify = () => {
    totpFormRef.value?.validate((valid) => {
        if (!valid) return
        state.submitLoading = true
        totpVerify({
            tempToken: state.tempToken,
            code: totpForm.code,
            keep: form.keep,
        })
            .then((res) => {
                navigateAfterLogin(res?.data?.userInfo)
            })
            .finally(() => {
                state.submitLoading = false
            })
    })
}

const onBindConfirm = () => {
    bindFormRef.value?.validate((valid) => {
        if (!valid) return
        state.submitLoading = true
        totpBindConfirm({
            tempToken: state.tempToken,
            secret: state.secret,
            code: totpForm.code,
            keep: form.keep,
        })
            .then((res) => {
                navigateAfterLogin(res?.data?.userInfo)
            })
            .finally(() => {
                state.submitLoading = false
            })
    })
}
</script>

<style scoped lang="scss">
.switch-language {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 1;
}
.bubble {
    overflow: hidden;
    background: url(/@/assets/bg.jpg) repeat;
}
.form-item-icon {
    height: auto;
}
.step-title {
    font-size: 18px;
    font-weight: 500;
    text-align: center;
    margin-bottom: 8px;
    color: var(--el-text-color-primary);
}
.step-desc {
    font-size: 13px;
    text-align: center;
    margin-bottom: 20px;
    color: var(--el-text-color-secondary);
    word-break: break-all;
}
.bind-panel {
    .qr-wrap {
        display: flex;
        justify-content: center;
        margin-bottom: 16px;
        min-height: 200px;
    }
    .qr-image {
        width: 200px;
        height: 200px;
        border: 1px solid var(--el-border-color);
        border-radius: 8px;
        background: #fff;
    }
    .secret-box {
        font-size: 12px;
        text-align: center;
        margin-bottom: 16px;
        padding: 8px;
        background: var(--el-fill-color-light);
        border-radius: 6px;
        word-break: break-all;
        .secret-label {
            display: block;
            color: var(--el-text-color-secondary);
            margin-bottom: 4px;
        }
        .secret-value {
            font-family: monospace;
            color: var(--el-text-color-primary);
        }
    }
}
.back-button {
    display: block;
    margin: 0 auto;
}
.login {
    position: absolute;
    top: 0;
    display: flex;
    width: 100vw;
    height: 100vh;
    align-items: center;
    justify-content: center;
    .login-box {
        overflow: hidden;
        width: 430px;
        padding: 0;
        background: var(--ba-bg-color-overlay);
        margin-bottom: 80px;
    }
    .head {
        background: #ccccff;
        img {
            display: block;
            width: 430px;
            margin: 0 auto;
            user-select: none;
        }
    }
    .form {
        position: relative;
        .profile-avatar {
            display: block;
            position: absolute;
            height: 100px;
            width: 100px;
            border-radius: 50%;
            border: 4px solid var(--ba-bg-color-overlay);
            top: -50px;
            right: calc(50% - 50px);
            z-index: 2;
            user-select: none;
        }
        .content {
            padding: 100px 40px 40px 40px;
        }
        .submit-button {
            width: 100%;
            letter-spacing: 2px;
            font-weight: 300;
            margin-top: 15px;
            --el-button-bg-color: var(--el-color-primary);
        }
    }
}

@media screen and (max-width: 720px) {
    .login {
        display: flex;
        align-items: center;
        justify-content: center;
        .login-box {
            width: 340px;
            margin-top: 0;
        }
    }
}
.chang-lang :deep(.el-dropdown-menu__item) {
    justify-content: center;
}
.content :deep(.el-input__prefix) {
    display: flex;
    align-items: center;
}

@at-root .dark {
    .bubble {
        background: url(/@/assets/bg-dark.jpg) repeat;
    }
    .login {
        .login-box {
            background: #161b22;
        }
        .head {
            img {
                filter: brightness(61%);
            }
        }
        .form {
            .submit-button {
                --el-button-bg-color: var(--el-color-primary-light-5);
                --el-button-border-color: rgba(240, 252, 241, 0.1);
            }
        }
    }
}
@media screen and (max-height: 800px) {
    .login .login-box {
        margin-bottom: 0;
    }
}
</style>
