<script setup lang="ts">
import { Check, Clock3, Eye, EyeOff, KeyRound, UserPlus, UserRound } from "@lucide/vue";
import { computed, onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { apiRequest, ApiError } from "../data/api";
import { useAuthStore, type SessionUser } from "../stores/auth";
import { useLedgerStore } from "../stores/ledgers";

type Invitation = {
  expiresAt: string;
  status: "valid" | "expired" | "accepted" | "revoked";
};

const route = useRoute();
const router = useRouter();
const auth = useAuthStore();
const ledgerStore = useLedgerStore();
const invitation = ref<Invitation | null>(null);
const name = ref("");
const password = ref("");
const passwordVisible = ref(false);
const loading = ref(true);
const accepting = ref(false);
const errorMessage = ref("");
const key = computed(() => typeof route.query.key === "string" ? route.query.key : "");
const expiresLabel = computed(() => invitation.value
  ? new Intl.DateTimeFormat("zh-CN", { month: "long", day: "numeric", hour: "2-digit", minute: "2-digit" }).format(new Date(invitation.value.expiresAt))
  : "");

onMounted(async () => {
  if (auth.user) {
    await router.replace("/");
    return;
  }
  if (!key.value) {
    errorMessage.value = "邀请链接缺少 key";
    loading.value = false;
    return;
  }
  try {
    const result = await apiRequest<{ invitation?: Invitation; user: SessionUser | null; alreadyLoggedIn?: boolean }>(
      `/api/invitations/${encodeURIComponent(key.value)}`,
    );
    if (result.alreadyLoggedIn || result.user) {
      await auth.loadSession(true);
      await router.replace("/");
      return;
    }
    invitation.value = result.invitation ?? null;
    if (result.invitation?.status !== "valid") {
      errorMessage.value = {
        expired: "邀请链接已过期",
        accepted: "邀请链接已被使用",
        revoked: "邀请链接已撤销",
        valid: "",
      }[result.invitation?.status ?? "revoked"];
    }
  } catch (error) {
    errorMessage.value = error instanceof ApiError ? error.message : "无法读取邀请";
  } finally {
    loading.value = false;
  }
});

async function acceptInvitation() {
  if (!invitation.value || accepting.value || !name.value.trim() || password.value.length < 8) return;
  accepting.value = true;
  errorMessage.value = "";
  try {
    await apiRequest(`/api/invitations/${encodeURIComponent(key.value)}/accept`, {
      method: "POST",
      body: { name: name.value.trim(), password: password.value },
    });
    password.value = "";
    await auth.loadSession(true);
    await ledgerStore.reloadLedgers();
    await router.replace("/");
  } catch (error) {
    errorMessage.value = error instanceof ApiError ? error.message : "创建账号失败";
  } finally {
    accepting.value = false;
  }
}
</script>

<template>
  <main class="app-shell invite-shell">
    <header><span><KeyRound :size="18" /></span><strong>Cents</strong></header>
    <section class="invite-content">
      <div v-if="loading" class="invite-state">正在验证邀请...</div>
      <template v-else-if="invitation">
        <span class="invite-mark"><UserPlus :size="30" /></span>
        <div class="invite-heading">
          <small>你被邀请使用</small>
          <h1>Cents 家庭账本</h1>
          <span><Clock3 :size="14" />有效期至 {{ expiresLabel }}</span>
        </div>

        <form v-if="invitation.status === 'valid'" @submit.prevent="acceptInvitation">
          <label><span>姓名</span><div><UserRound :size="19" /><input v-model="name" maxlength="40" autocomplete="username" placeholder="也是你的登录名" autofocus /></div></label>
          <label><span>密码</span><div><KeyRound :size="19" /><input v-model="password" :type="passwordVisible ? 'text' : 'password'" minlength="8" maxlength="128" autocomplete="new-password" placeholder="至少 8 位" /><button type="button" :aria-label="passwordVisible ? '隐藏密码' : '显示密码'" :title="passwordVisible ? '隐藏密码' : '显示密码'" @click="passwordVisible = !passwordVisible"><EyeOff v-if="passwordVisible" :size="19" /><Eye v-else :size="19" /></button></div></label>
          <p v-if="errorMessage" class="form-error" role="alert">{{ errorMessage }}</p>
          <button class="invite-submit" type="submit" :disabled="accepting || !name.trim() || password.length < 8"><Check :size="19" />{{ accepting ? "正在创建" : "创建账号" }}</button>
        </form>
        <div v-else class="invite-error">{{ errorMessage }}</div>
      </template>
      <div v-else class="invite-error">{{ errorMessage }}</div>
    </section>
  </main>
</template>

<style scoped>
.invite-shell { background:#f4f8f7; }
.invite-shell > header { height:64px; display:flex; align-items:center; justify-content:center; gap:8px; border-bottom:1px solid #dce8e5; background:#fff; color:#087f72; }
.invite-shell > header span { width:30px; height:30px; display:grid; place-items:center; border-radius:50%; background:#e5f5f1; }
.invite-content { height:calc(100% - 64px); display:flex; flex-direction:column; align-items:center; padding:38px 24px 28px; overflow-y:auto; }
.invite-mark { width:68px; height:68px; display:grid; place-items:center; border:1px solid #b8dad2; border-radius:50%; background:#fff; color:#087f72; box-shadow:0 12px 30px rgba(34,78,68,.12); }
.invite-heading { display:grid; justify-items:center; gap:5px; margin-top:17px; text-align:center; }
.invite-heading small { color:#6f7e7a; font-size:13px; }
.invite-heading h1 { margin:0; color:#1d302b; font-size:24px; letter-spacing:0; }
.invite-heading span { display:flex; align-items:center; gap:5px; color:#71827d; font-size:12px; }
.invite-content form { width:100%; display:grid; gap:14px; margin-top:25px; }
.invite-content label { display:grid; gap:6px; color:#64746f; font-size:12px; }
.invite-content label > div { height:50px; display:grid; grid-template-columns:30px minmax(0,1fr) auto; align-items:center; border:1px solid #cbdad6; border-radius:8px; padding:0 11px; background:#fff; color:#70817c; }
.invite-content input { min-width:0; border:0; outline:0; color:#1e312c; font-size:16px; }
.invite-content label button { width:38px; height:38px; display:grid; place-items:center; border:0; background:transparent; color:#61756f; }
.invite-submit { height:50px; display:flex; align-items:center; justify-content:center; gap:7px; border:0; border-radius:8px; background:#087f72; color:#fff; font-weight:720; }
.invite-submit:disabled { opacity:.5; }
.form-error { margin:0; color:#c74d39; font-size:12px; }
.invite-state,.invite-error { margin:auto; color:#6e7f7a; font-size:14px; text-align:center; }
.invite-error { color:#c74d39; }
</style>
