<script setup lang="ts">
import { ref, shallowReadonly, useTemplateRef, watchEffect } from 'vue'
import type { FormInst, FormRules, FormItemRule, CountdownInst } from 'naive-ui'
import { Mail, Lock, Iphone, Down, User } from '@icon-park/vue-next'
import isMobilePhone from 'validator/es/lib/isMobilePhone'
import isEmail from 'validator/es/lib/isEmail'
import { ss } from '@/utils/storage'
import { fetchEmailCode, fetchLogin, fetchSMSCode } from '@/apis/user'
import SparkMD5 from 'spark-md5'
import { useUserStore } from '@/store/modules/user'
import type { UserInfo } from '@/store/types/user'
import { useRouter, useRoute } from 'vue-router'

enum StorageKeyEnum {
  smsCountdownTime = 'SMS_COUNTDOWN_TIME',
  emailCountdownTime = 'MAIL_COUNTDOWN_TIME',
}
type LoginMethod = 'password' | 'sms' | 'email'
interface LoginPayload {
  loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW'
  account: string
  password?: string
  authCode?: string
}

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

const passwordLoginFormRef = useTemplateRef<FormInst>('passwordLoginFormRef')
const smsLoginFormRef = useTemplateRef<FormInst>('smsLoginFormRef')
const emailLoginFormRef = useTemplateRef<FormInst>('emailLoginFormRef')
const countdownRef = useTemplateRef<CountdownInst>('countdownRef')

const currentLoginMethod = ref<LoginMethod>('password')
const showCardReserveAnimation = ref(false)

const loginBtnLoading = ref(false)

const passwordLoginForm = ref({
  account: '',
  password: '',
})
const smsLoginForm = ref({
  phoneNumber: '',
  code: '',
})
const emailLoginForm = ref({
  email: '',
  code: '',
})

const passwordLoginFormRules = shallowReadonly<FormRules>({
  account: { required: true, message: '請輸入用戶名', trigger: 'blur' },
  password: { required: true, message: '請輸入密碼', trigger: 'blur' },
})
const smsLoginFormRules = shallowReadonly<FormRules>({
  phoneNumber: {
    key: 'phoneNumber',
    required: true,
    validator: (_rule: FormItemRule, value: string) => {
      if (!value) {
        return new Error('請輸入手機號')
      } else if (!isMobilePhone(value, ['zh-CN', 'zh-HK'])) {
        return new Error('請輸入正確手機號')
      }

      return
    },
  },
  code: { required: true, message: '請輸入驗証碼' },
})
const emailLoginFormRules = shallowReadonly<FormRules>({
  email: {
    key: 'email',
    required: true,
    validator: (_rule: FormItemRule, value: string) => {
      if (!value) {
        return new Error('請輸入郵箱地址')
      } else if (!isEmail(value)) {
        return new Error('請輸入正確郵箱地址')
      }

      return
    },
  },
  code: { required: true, message: '請輸入驗証碼' },
})

const phoneNumberAreaOptions = shallowReadonly([
  {
    label: '+86 中國大陸',
    value: '+86',
  },
  {
    label: '+852 中國香港',
    value: '+852',
  },
])
const currentPhoneNumberArea = ref<'+86' | '+852'>('+852')

const countdownActive = ref(true)
const isShowCountdown = ref(false)
const countdownDuration = ref<number>(60000)

watchEffect(() => {
  let timeStringDraft = ''

  switch (currentLoginMethod.value) {
    case 'sms':
      {
        timeStringDraft = ss.get(StorageKeyEnum.smsCountdownTime)
      }
      break
    case 'email':
      {
        timeStringDraft = ss.get(StorageKeyEnum.emailCountdownTime)
      }
      break
  }

  if (timeStringDraft) {
    const time = Math.floor(Date.now() - parseInt(timeStringDraft))

    if (time < 60000) {
      countdownDuration.value = 60000 - time
      countdownRef.value?.reset()
      isShowCountdown.value = true
    }
  }
})

function onlyAllowNumber(value: string) {
  return !value || /^\d+$/.test(value)
}
function noSideSpace(value: string) {
  return !value.startsWith(' ') && !value.endsWith(' ')
}

function countdownRender({ seconds, minutes }: { seconds: number; minutes: number }) {
  if (minutes && minutes === 1) {
    return '60 s'
  }

  return `${seconds} s`
}

function onCardReserveAnimationEnd() {
  showCardReserveAnimation.value = false
}

function onCountdownFinish() {
  isShowCountdown.value = false
}

function getInputPhoneNumber() {
  return currentPhoneNumberArea.value !== '+86'
    ? `${currentPhoneNumberArea.value}${smsLoginForm.value.phoneNumber}`
    : smsLoginForm.value.phoneNumber
}

function handleLoginSubmit(method: LoginMethod) {
  let payload: LoginPayload = {
    loginChannel: 'MEMBER_PLATFOMR_PW',
    account: '',
    password: '',
  }

  new Promise((resolve) => {
    switch (method) {
      case 'password':
        {
          passwordLoginFormRef.value?.validate((errors) => {
            if (errors) return ''

            payload = {
              loginChannel: 'MEMBER_PLATFOMR_PW',
              account: passwordLoginForm.value.account,
              password: SparkMD5.hash(passwordLoginForm.value.password),
            }

            resolve(true)
          })
        }
        break
      case 'sms':
        {
          smsLoginFormRef.value?.validate((errors) => {
            if (errors) return ''

            payload = {
              loginChannel: 'MEMBER_PLATFOMR_SMS',
              account: getInputPhoneNumber(),
              authCode: smsLoginForm.value.code,
            }

            resolve(true)
          })
        }
        break
      case 'email':
        {
          emailLoginFormRef.value?.validate((errors) => {
            if (errors) return ''

            payload = {
              loginChannel: 'MEMBER_PLATFOMR_EMAIL',
              account: emailLoginForm.value.email,
              authCode: emailLoginForm.value.code,
            }

            resolve(true)
          })
        }
        break
    }
  }).then(() => {
    loginBtnLoading.value = true

    fetchLogin<UserInfo & { token: string }>(payload)
      .then((res) => {
        if (res.code !== 0) return ''

        userStore.updateToken(res.data.token)
        userStore.updateUserInfo({
          avatarUrl: res.data.avatarUrl,
          memberId: res.data.memberId,
          mobilePhone: res.data.mobilePhone,
          nickName: res.data.nickName,
        })

        const redirectUrl = decodeURIComponent((route.query.redirect as string) || '')
        router.replace({ path: redirectUrl ? redirectUrl : '/' })

        window.$message.success('登錄成功')

        ss.remove(StorageKeyEnum.smsCountdownTime)
        ss.remove(StorageKeyEnum.emailCountdownTime)
      })
      .finally(() => {
        loginBtnLoading.value = false
      })
  })
}

function handleLoginMethodChange(method: LoginMethod) {
  showCardReserveAnimation.value = true

  setTimeout(() => {
    currentLoginMethod.value = method
  }, 500)
}

function handleSMSCodeGain() {
  smsLoginFormRef.value?.validate(
    (errors) => {
      if (errors) return ''

      countdownDuration.value = 60000
      ss.set(StorageKeyEnum.smsCountdownTime, Date.now())
      countdownRef.value?.reset()
      isShowCountdown.value = true

      fetchSMSCode(getInputPhoneNumber()).then((res) => {
        if (res.code !== 0) return ''

        window.$message.success('獲取成功')
      })
    },
    (rule) => {
      return rule.key === 'phoneNumber'
    },
  )
}

function handleEmailCodeGain() {
  emailLoginFormRef.value?.validate(
    (errors) => {
      if (errors) return ''

      countdownDuration.value = 60000
      ss.set(StorageKeyEnum.emailCountdownTime, Date.now())
      countdownRef.value?.reset()
      isShowCountdown.value = true

      fetchEmailCode(encodeURIComponent(emailLoginForm.value.email)).then((res) => {
        if (res.code !== 0) return ''

        window.$message.success('獲取成功')
      })
    },
    (rule) => {
      return rule.key === 'email'
    },
  )
}
</script>

<template>
  <div class="bg-px-login-bg-png relative h-screen min-h-[750px] w-full min-w-[600px] bg-cover bg-center bg-no-repeat">
    <div
      class="bg-px-logo-png z-100 absolute left-[60px] top-[25px] h-[29px] w-[119px] bg-contain bg-center bg-no-repeat"
    ></div>

    <div class="absolute right-[14%] top-1/2 h-[458px] w-[390px] -translate-y-1/2">
      <div
        class="h-full w-full rounded-[10px] bg-[#fff] px-[29px] shadow-2xl"
        :class="{ 'animate-card-reverse': showCardReserveAnimation }"
        style="transform-style: preserve-3d"
        @animationend="onCardReserveAnimationEnd"
      >
        <h1 class="font-600 py-[34px] text-center text-[22px]">歡迎使用萃想POC</h1>

        <div>
          <!-- 密码登录 -->
          <n-form
            v-if="currentLoginMethod === 'password'"
            ref="passwordLoginFormRef"
            label-placement="left"
            size="large"
            :model="passwordLoginForm"
            :rules="passwordLoginFormRules"
          >
            <n-form-item path="account">
              <n-input
                v-model:value="passwordLoginForm.account"
                :allow-input="noSideSpace"
                :maxlength="11"
                placeholder="請輸入用戶名"
              >
                <template #prefix>
                  <div class="mr-[6px]">
                    <User theme="outline" size="16" fill="#868686" :stroke-width="3" />
                  </div>
                </template>
              </n-input>
            </n-form-item>
            <n-form-item path="password">
              <n-input
                v-model:value="passwordLoginForm.password"
                class="font-sans"
                :allow-input="noSideSpace"
                type="password"
                show-password-on="click"
                placeholder="請輸入密碼"
              >
                <template #prefix>
                  <div class="mr-[6px]">
                    <Lock theme="outline" size="16" fill="#868686" :stroke-width="3" />
                  </div>
                </template>
              </n-input>
            </n-form-item>
            <n-form-item class="mt-4">
              <n-button
                type="primary"
                size="large"
                block
                :loading="loginBtnLoading"
                :disabled="!passwordLoginForm.account || !passwordLoginForm.password"
                @click="handleLoginSubmit('password')"
              >
                登錄
              </n-button>
            </n-form-item>
          </n-form>

          <!-- SMS登录 -->
          <n-form
            v-if="currentLoginMethod === 'sms'"
            ref="smsLoginFormRef"
            label-placement="left"
            size="large"
            :model="smsLoginForm"
            :rules="smsLoginFormRules"
          >
            <n-form-item path="phoneNumber">
              <n-input
                v-model:value.trim="smsLoginForm.phoneNumber"
                :allow-input="onlyAllowNumber"
                :maxlength="currentPhoneNumberArea === '+852' ? 8 : 11"
                placeholder="請輸入手機號"
              >
                <template #prefix>
                  <div class="flex items-center">
                    <n-popselect
                      v-model:value="currentPhoneNumberArea"
                      :options="phoneNumberAreaOptions"
                      trigger="click"
                    >
                      <div class="flex w-[62px] cursor-pointer items-center">
                        <div class="mr-[4px]">{{ currentPhoneNumberArea }}</div>

                        <Down theme="outline" size="18" fill="#333" :stroke-width="3" />
                      </div>
                    </n-popselect>

                    <div class="mx-[8px] h-[18px] w-[1px] bg-[#868686]"></div>
                  </div>
                </template>
              </n-input>
            </n-form-item>
            <n-form-item path="code">
              <n-input
                v-model:value="smsLoginForm.code"
                :allow-input="onlyAllowNumber"
                show-password-on="click"
                :maxlength="6"
                placeholder="請輸入驗証碼"
              >
                <template #suffix>
                  <div class="flex items-center">
                    <div class="mx-[6px] h-[18px] w-[1px] bg-[#868686]"></div>

                    <div class="w-[90px] text-end">
                      <n-button
                        v-show="!isShowCountdown"
                        class="!text-[11px]"
                        type="tertiary"
                        size="small"
                        @click="handleSMSCodeGain"
                      >
                        獲取驗証碼
                      </n-button>

                      <div v-show="isShowCountdown" class="inline-block w-[50px] text-center">
                        <n-countdown
                          ref="countdownRef"
                          :duration="countdownDuration"
                          :active="countdownActive"
                          :render="countdownRender"
                          :on-finish="onCountdownFinish"
                        />
                      </div>
                    </div>
                  </div>
                </template>
              </n-input>
            </n-form-item>
            <n-form-item class="mt-4">
              <n-button
                type="primary"
                size="large"
                block
                :loading="loginBtnLoading"
                :disabled="!smsLoginForm.phoneNumber || !smsLoginForm.code"
                @click="handleLoginSubmit('sms')"
              >
                登錄
              </n-button>
            </n-form-item>
          </n-form>

          <!-- 邮箱登录 -->
          <n-form
            v-if="currentLoginMethod === 'email'"
            ref="emailLoginFormRef"
            label-placement="left"
            size="large"
            :model="emailLoginForm"
            :rules="emailLoginFormRules"
          >
            <n-form-item path="email">
              <n-input v-model:value="emailLoginForm.email" :allow-input="noSideSpace" placeholder="請輸入郵箱地址">
                <template #prefix>
                  <div class="mr-[6px]">
                    <Mail theme="outline" size="16" fill="#868686" :stroke-width="3" />
                  </div>
                </template>
              </n-input>
            </n-form-item>
            <n-form-item path="code">
              <n-input
                v-model:value="emailLoginForm.code"
                :allow-input="onlyAllowNumber"
                show-password-on="click"
                :maxlength="6"
                placeholder="請輸入驗証碼"
              >
                <template #suffix>
                  <div class="flex items-center">
                    <div class="mx-[6px] h-[18px] w-[1px] bg-[#868686]"></div>

                    <div class="w-[90px] text-end">
                      <n-button
                        v-show="!isShowCountdown"
                        class="!text-[11px]"
                        type="tertiary"
                        size="small"
                        @click="handleEmailCodeGain"
                      >
                        獲取驗証碼
                      </n-button>

                      <div v-show="isShowCountdown" class="inline-block w-[50px] text-center">
                        <n-countdown
                          ref="countdownRef"
                          :duration="countdownDuration"
                          :active="countdownActive"
                          :render="countdownRender"
                          :on-finish="onCountdownFinish"
                        />
                      </div>
                    </div>
                  </div>
                </template>
              </n-input>
            </n-form-item>
            <n-form-item class="mt-4">
              <n-button
                type="primary"
                size="large"
                block
                :loading="loginBtnLoading"
                :disabled="!emailLoginForm.email || !emailLoginForm.code"
                @click="handleLoginSubmit('email')"
              >
                登錄
              </n-button>
            </n-form-item>
          </n-form>
        </div>

        <div class="absolute bottom-[22px] left-0 w-full">
          <div class="mb-[32px]">
            <div class="mb-[12px] text-center text-[12px] text-[#999999]">其他登錄方式</div>
            <div class="flex items-center justify-center">
              <button
                v-show="currentLoginMethod !== 'email'"
                class="mx-[10px] flex h-[34px] w-[34px] items-center justify-center rounded-full bg-[#f1f1f1] transition hover:bg-[#e0e0e0]"
                @click="handleLoginMethodChange('email')"
              >
                <Mail theme="outline" size="18" fill="#666666" :stroke-width="3" />
              </button>

              <button
                v-show="currentLoginMethod !== 'sms'"
                class="mx-[10px] flex h-[34px] w-[34px] items-center justify-center rounded-full bg-[#f1f1f1] transition hover:bg-[#e0e0e0]"
                @click="handleLoginMethodChange('sms')"
              >
                <Iphone theme="outline" size="17" fill="#666666" :stroke-width="3" />
              </button>

              <button
                v-show="currentLoginMethod !== 'password'"
                class="mx-[10px] flex h-[34px] w-[34px] items-center justify-center rounded-full bg-[#f1f1f1] transition hover:bg-[#e0e0e0]"
                @click="handleLoginMethodChange('password')"
              >
                <Lock theme="outline" size="17" fill="#666666" :stroke-width="3" />
              </button>
            </div>
          </div>

          <!-- <div class="text-center">
            <n-checkbox size="small"><span class="text-[12px]">閱讀並同意協議</span></n-checkbox>
          </div> -->
        </div>
      </div>
    </div>
  </div>
</template>
