<script setup lang="ts">
import {
  CircleHelp,
  GraduationCap,
  RefreshCw,
  ShieldCheck,
  Smartphone,
  X,
} from "@lucide/vue";
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue";

import { api, ApiError, saveToken } from "../services/api";
import type { UserProfile } from "../types/api";
import AppDialog from "./AppDialog.vue";
import HelpDialog from "./HelpDialog.vue";
import PolicyDialog from "./PolicyDialog.vue";

const emit = defineEmits<{
  loggedIn: [user: UserProfile];
}>();

const COUNTDOWN_SECONDS = 60;
const COUNTDOWN_PREFIX = "ai-kb-sms-countdown:";
const PHONE_STORAGE_KEY = "ai-kb-login-phone";
const PHONE_PATTERN = /^1[3-9]\d{9}$/;

const phone = ref("");
const code = ref("");
const phoneTouched = ref(false);
const sending = ref(false);
const loggingIn = ref(false);
const countdown = ref(0);
const agreed = ref(false);
const agreementAttention = ref(false);
const activePolicy = ref<"agreement" | "privacy" | null>(null);
const helpMode = ref<"sms" | "login" | null>(null);
const toast = ref("");

const captchaOpen = ref(false);
const captchaId = ref("");
const captchaCode = ref("");
const captchaImage = ref("");
const captchaLoading = ref(false);
const captchaSubmitting = ref(false);
const captchaError = ref("");

const supportContact = import.meta.env.VITE_SUPPORT_CONTACT?.trim() ?? "";
const phoneValid = computed(() => PHONE_PATTERN.test(phone.value));
const phoneError = computed(() => phoneTouched.value && phone.value.length > 0 && !phoneValid.value);
const loginFieldsValid = computed(() => phoneValid.value && code.value.length >= 4);
const sendButtonText = computed(() => {
  if (sending.value) return "发送中...";
  if (countdown.value > 0) return `${countdown.value}s 后重发`;
  return "发送验证码";
});

let countdownTimer: ReturnType<typeof setInterval> | null = null;
let toastTimer: ReturnType<typeof setTimeout> | null = null;

onMounted(() => {
  const storedPhone = window.localStorage.getItem(PHONE_STORAGE_KEY) ?? "";
  if (PHONE_PATTERN.test(storedPhone)) phone.value = storedPhone;
  restoreCountdown();
});
onUnmounted(() => {
  stopCountdownTimer();
  if (toastTimer) clearTimeout(toastTimer);
});

watch(phone, () => {
  code.value = sanitizeDigits(code.value, 6);
  if (phoneValid.value) window.localStorage.setItem(PHONE_STORAGE_KEY, phone.value);
  restoreCountdown();
});

function onPhoneInput(event: Event) {
  phone.value = sanitizeDigits((event.target as HTMLInputElement).value, 11);
}

function onCodeInput(event: Event) {
  code.value = sanitizeDigits((event.target as HTMLInputElement).value, 6);
}

function sanitizeDigits(value: string, maxLength: number) {
  return value.replace(/\D/g, "").slice(0, maxLength);
}

function clearPhone() {
  phone.value = "";
  phoneTouched.value = false;
  window.localStorage.removeItem(PHONE_STORAGE_KEY);
}

async function sendCode() {
  if (sending.value || countdown.value > 0) return;
  phoneTouched.value = true;
  if (!phoneValid.value) {
    showToast("请输入正确的手机号码");
    return;
  }

  sending.value = true;
  try {
    await api.sendSms(phone.value);
    completeSmsSend();
  } catch (error) {
    if (error instanceof ApiError && error.status === 428) {
      await openCaptcha();
    } else {
      showToast(errorMessage(error, "验证码发送失败，请稍后重试"));
    }
  } finally {
    sending.value = false;
  }
}

async function openCaptcha() {
  captchaOpen.value = true;
  captchaError.value = "";
  await loadCaptcha();
}

async function loadCaptcha() {
  if (captchaLoading.value) return;
  captchaLoading.value = true;
  captchaError.value = "";
  try {
    const result = await api.captcha();
    captchaId.value = result.captchaId;
    captchaImage.value = result.imageBase64;
    captchaCode.value = "";
  } catch (error) {
    captchaError.value = errorMessage(error, "图形验证码加载失败，请重试");
  } finally {
    captchaLoading.value = false;
  }
}

async function submitCaptcha() {
  if (captchaSubmitting.value) return;
  const normalizedCode = captchaCode.value.trim();
  if (normalizedCode.length < 4) {
    captchaError.value = "请输入图形验证码";
    return;
  }

  captchaSubmitting.value = true;
  captchaError.value = "";
  try {
    await api.sendSms(phone.value, captchaId.value, normalizedCode);
    captchaOpen.value = false;
    completeSmsSend();
  } catch (error) {
    captchaError.value = errorMessage(error, "图形验证失败，请重试");
    if (/图形验证码|过期/.test(captchaError.value)) await loadCaptcha();
  } finally {
    captchaSubmitting.value = false;
  }
}

function completeSmsSend() {
  persistCountdown(phone.value, Date.now() + COUNTDOWN_SECONDS * 1000);
  restoreCountdown();
  showToast("验证码已发送，请注意查收");
}

async function login() {
  if (loggingIn.value || !loginFieldsValid.value) return;
  if (!agreed.value) {
    agreementAttention.value = true;
    showToast("请先阅读并同意用户协议和隐私政策");
    window.setTimeout(() => (agreementAttention.value = false), 1800);
    return;
  }

  loggingIn.value = true;
  try {
    const result = await api.login(phone.value, code.value);
    saveToken(result.token);
    showToast("登录成功");
    emit("loggedIn", result.user);
  } catch (error) {
    showToast(errorMessage(error, "登录失败，请稍后重试"));
  } finally {
    loggingIn.value = false;
  }
}

function countdownKey(value = phone.value) {
  return `${COUNTDOWN_PREFIX}${value}`;
}

function persistCountdown(value: string, expiresAt: number) {
  window.localStorage.setItem(countdownKey(value), String(expiresAt));
}

function restoreCountdown() {
  stopCountdownTimer();
  if (!phoneValid.value) {
    countdown.value = 0;
    return;
  }
  updateCountdown();
  if (countdown.value > 0) countdownTimer = setInterval(updateCountdown, 1000);
}

function updateCountdown() {
  const stored = Number(window.localStorage.getItem(countdownKey()) ?? 0);
  const remaining = Math.max(0, Math.ceil((stored - Date.now()) / 1000));
  countdown.value = remaining;
  if (remaining <= 0) {
    window.localStorage.removeItem(countdownKey());
    stopCountdownTimer();
  }
}

function stopCountdownTimer() {
  if (countdownTimer) clearInterval(countdownTimer);
  countdownTimer = null;
}

function showToast(text: string) {
  toast.value = text;
  if (toastTimer) clearTimeout(toastTimer);
  toastTimer = setTimeout(() => (toast.value = ""), 3000);
}

function errorMessage(error: unknown, fallback: string) {
  return error instanceof Error ? error.message : fallback;
}

async function keepFieldVisible(event: FocusEvent) {
  await nextTick();
  (event.target as HTMLElement).scrollIntoView({ block: "center", behavior: "smooth" });
}
</script>

<template>
  <section class="login-page">
    <div class="login-content">
      <header class="login-brand-area">
        <div class="brand-heading">
          <span class="brand-icon" aria-hidden="true"><GraduationCap :size="30" stroke-width="2.2" /></span>
          <h1>大本营答疑助手</h1>
        </div>
        <p>登录后即可开始提问，快速找到你需要的答案</p>
        <ShieldCheck class="brand-watermark" :size="150" stroke-width="1.2" aria-hidden="true" />
      </header>

      <form class="login-card" novalidate @submit.prevent="login">
        <div class="form-field">
          <label for="login-phone">手机号</label>
          <div class="input-shell" :class="{ invalid: phoneError }">
            <Smartphone :size="20" aria-hidden="true" />
            <input
              id="login-phone"
              :value="phone"
              type="tel"
              inputmode="numeric"
              autocomplete="tel"
              maxlength="11"
              placeholder="请输入手机号"
              @input="onPhoneInput"
              @blur="phoneTouched = true"
              @focus="keepFieldVisible"
            />
            <button v-if="phone" type="button" class="input-clear" aria-label="清空手机号" @click="clearPhone">
              <X :size="17" aria-hidden="true" />
            </button>
          </div>
          <p v-if="phoneError" class="field-error">请输入正确的手机号码</p>
        </div>

        <div class="form-field">
          <label for="login-code">短信验证码</label>
          <div class="input-shell code-input-shell">
            <ShieldCheck :size="20" aria-hidden="true" />
            <input
              id="login-code"
              :value="code"
              type="text"
              inputmode="numeric"
              autocomplete="one-time-code"
              maxlength="6"
              placeholder="请输入短信验证码"
              @input="onCodeInput"
              @focus="keepFieldVisible"
            />
            <span class="input-divider" aria-hidden="true"></span>
            <button
              type="button"
              class="send-code-button"
              :disabled="!phoneValid || sending || countdown > 0"
              @click="sendCode"
            >
              {{ sendButtonText }}
            </button>
          </div>
          <p class="security-hint"><ShieldCheck :size="16" aria-hidden="true" />为保障账号安全，必要时将进行图形验证</p>
        </div>

        <div class="agreement-row" :class="{ attention: agreementAttention }">
          <label class="agreement-toggle">
            <input v-model="agreed" type="checkbox" />
            <span>我已阅读并同意</span>
          </label>
          <button type="button" class="policy-link" @click="activePolicy = 'agreement'">《用户协议》</button>
          <span aria-hidden="true">和</span>
          <button type="button" class="policy-link" @click="activePolicy = 'privacy'">《隐私政策》</button>
        </div>

        <button type="submit" class="login-submit" :disabled="!loginFieldsValid || loggingIn">
          {{ loggingIn ? "正在进入..." : "立即进入" }}
        </button>

        <div class="login-help-links">
          <button type="button" @click="helpMode = 'sms'">收不到验证码？</button>
          <span aria-hidden="true"></span>
          <button type="button" @click="helpMode = 'login'">登录遇到问题</button>
        </div>
      </form>

      <footer class="login-security-footer">
        <span></span><ShieldCheck :size="20" aria-hidden="true" /><span></span>
        <p>仅用于身份验证与账号登录，不会泄露你的信息</p>
      </footer>
    </div>

    <div v-if="toast" class="login-toast" role="status" aria-live="polite">{{ toast }}</div>

    <AppDialog v-if="captchaOpen" title="安全验证" labelled-by="captcha-dialog-title" @close="captchaOpen = false">
      <div class="captcha-dialog-copy">
        <p>请完成图形验证后继续发送短信验证码。</p>
        <div class="captcha-entry">
          <input
            v-model.trim="captchaCode"
            type="text"
            inputmode="text"
            autocomplete="off"
            maxlength="8"
            placeholder="请输入图形验证码"
            @keyup.enter="submitCaptcha"
          />
          <button type="button" :disabled="captchaLoading" aria-label="刷新图形验证码" @click="loadCaptcha">
            <img v-if="captchaImage" :src="captchaImage" alt="图形验证码，点击刷新" />
            <RefreshCw v-else :size="20" :class="{ spinning: captchaLoading }" aria-hidden="true" />
          </button>
        </div>
        <p class="captcha-refresh-tip"><CircleHelp :size="15" aria-hidden="true" />看不清？点击图片刷新</p>
        <p v-if="captchaError" class="field-error">{{ captchaError }}</p>
      </div>
      <template #footer>
        <button type="button" class="dialog-confirm" :disabled="captchaSubmitting" @click="submitCaptcha">
          {{ captchaSubmitting ? "验证中..." : "确认并发送" }}
        </button>
      </template>
    </AppDialog>

    <PolicyDialog v-if="activePolicy" :policy="activePolicy" @close="activePolicy = null" />
    <HelpDialog v-if="helpMode" :mode="helpMode" :contact="supportContact" @close="helpMode = null" />
  </section>
</template>
