<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";

import AppDialog from "./components/AppDialog.vue";
import ChatComposer from "./components/ChatComposer.vue";
import ChatHeader from "./components/ChatHeader.vue";
import LoginPanel from "./components/LoginPanel.vue";
import MessageList, { type DisplayMessage } from "./components/MessageList.vue";
import SessionDrawer from "./components/SessionDrawer.vue";
import SessionQuota from "./components/SessionQuota.vue";
import { ApiError, api, clearToken, getToken, streamChat } from "./services/api";
import type { ChatMessage as ApiMessage, ChatSession, GrowthProfileResult, PeriodicReport, ShareDraft, TeacherHelpCard, UserProfile } from "./types/api";

const user = ref<UserProfile | null>(null);
const sessions = ref<ChatSession[]>([]);
const activeSessionId = ref<number | null>(null);
const messages = ref<DisplayMessage[]>([]);
const drawerOpen = ref(false);
const sending = ref(false);
const booting = ref(true);
const loadingSession = ref(false);
const loadingSessions = ref(false);
const sessionOperationPending = ref(false);
const logoutDialogOpen = ref(false);
const profileDialogOpen = ref(false);
const helpCardDialogOpen = ref(false);
const shareDraftDialogOpen = ref(false);
const growthProfile = ref<GrowthProfileResult | null>(null);
const helpCard = ref<TeacherHelpCard | null>(null);
const helpCardContent = ref("");
const shareDraft = ref<ShareDraft | null>(null);
const shareDraftContent = ref("");
const helpCardHistory = ref<TeacherHelpCard[]>([]);
const shareDraftHistory = ref<ShareDraft[]>([]);
const reportHistory = ref<PeriodicReport[]>([]);
const profileLoading = ref(false);
const finishingTopic = ref(false);
const generatingHelpCard = ref(false);
const generatingShareDraft = ref(false);
const statusText = ref("连接后端中");
const toastText = ref("");
const followingOutput = ref(true);
const messageList = ref<InstanceType<typeof MessageList> | null>(null);
const activeAbortController = ref<AbortController | null>(null);
let toastTimer: number | null = null;

onMounted(async () => {
  if (!getToken()) {
    booting.value = false;
    statusText.value = "请先登录";
    return;
  }
  try {
    user.value = await api.profile();
    await loadSessions();
    statusText.value = "已连接大本营答疑服务";
  } catch (error) {
    handleError(error, "页面初始化失败");
  } finally {
    booting.value = false;
  }
});

onBeforeUnmount(() => {
  activeAbortController.value?.abort();
  if (toastTimer) window.clearTimeout(toastTimer);
  document.body.classList.remove("drawer-open");
});

async function onLoggedIn(profile: UserProfile) {
  user.value = profile;
  statusText.value = "已连接大本营答疑服务";
  try {
    await loadSessions();
  } catch (error) {
    handleError(error, "会话加载失败");
  }
}

async function loadSessions() {
  loadingSessions.value = true;
  try {
    sessions.value = await api.listSessions();
    if (sessions.value.length === 0) {
      await createSession();
      return;
    }
    await selectSession(sessions.value[0].id, true);
  } finally {
    loadingSessions.value = false;
  }
}

async function createSession() {
  if (sessionOperationPending.value) return;
  const existingBlank = sessions.value.find((session) => session.messageCount === 0);
  if (existingBlank) {
    await selectSession(existingBlank.id);
    return;
  }
  sessionOperationPending.value = true;
  try {
    const result = await api.createSession();
    sessions.value = await api.listSessions();
    await selectSession(result.sessionId, true);
  } catch (error) {
    handleError(error, "新建会话失败");
  } finally {
    sessionOperationPending.value = false;
  }
}

async function selectSession(sessionId: number, force = false) {
  if (!force && activeSessionId.value === sessionId && !loadingSession.value) {
    drawerOpen.value = false;
    return;
  }
  loadingSession.value = true;
  try {
    const history = await api.history(sessionId);
    activeSessionId.value = sessionId;
    messages.value = history.map(toUiMessage);
    followingOutput.value = true;
    drawerOpen.value = false;
    await messageList.value?.scrollToBottom("auto");
    await refreshProfile();
  } catch (error) {
    handleError(error, "会话加载失败");
  } finally {
    loadingSession.value = false;
  }
}

async function send(message: string, complete: (success: boolean) => void) {
  if (!activeSessionId.value || sending.value) {
    complete(false);
    return;
  }
  const now = new Date().toISOString();
  const userMessage: DisplayMessage = { id: `user-${Date.now()}`, role: "user", content: message, createdAt: now };
  const assistantMessage: DisplayMessage = {
    id: `assistant-${Date.now()}`,
    role: "assistant",
    content: "",
    createdAt: now,
    streaming: true,
  };
  messages.value.push(userMessage, assistantMessage);
  const assistantIndex = messages.value.length - 1;
  const currentAssistant = () => messages.value[assistantIndex];
  sending.value = true;
  followingOutput.value = true;
  activeAbortController.value = new AbortController();
  let hasContent = false;
  await messageList.value?.scrollToMessage(userMessage.id);
  try {
    await streamChat(
      activeSessionId.value,
      message,
      async (chunk) => {
        if (!hasContent) {
          currentAssistant().content = "";
          hasContent = true;
        }
        currentAssistant().content += chunk;
        if (followingOutput.value) await messageList.value?.scrollToBottom();
      },
      async (chunk) => {
        currentAssistant().reasoning = (currentAssistant().reasoning || "") + chunk;
        if (followingOutput.value) await messageList.value?.scrollToBottom();
      },
      async (statusMessage, statusType, reasoningVisible) => {
        if (statusType === "queued") currentAssistant().content = statusMessage || "当前请求较多，正在排队中。";
        if (statusType === "generating") {
          currentAssistant().showReasoning = reasoningVisible;
          if (!hasContent) currentAssistant().content = "";
        }
        if (followingOutput.value) await messageList.value?.scrollToBottom();
      },
      activeAbortController.value.signal,
    );
    currentAssistant().streaming = false;
    currentAssistant().createdAt = new Date().toISOString();
    complete(true);
    await refreshSessionList();
    await refreshProfile();
  } catch (error) {
    currentAssistant().streaming = false;
    if (error instanceof DOMException && error.name === "AbortError") {
      currentAssistant().content = currentAssistant().content || "已停止生成";
      complete(true);
    } else {
      currentAssistant().content = currentAssistant().content || "回答生成失败，请稍后重试。";
      complete(false);
      handleError(error, "AI 回复失败");
    }
  } finally {
    sending.value = false;
    activeAbortController.value = null;
  }
}

async function stop() {
  if (!activeSessionId.value || !sending.value) return;
  activeAbortController.value?.abort();
  try {
    await api.stop(activeSessionId.value);
  } catch (error) {
    handleError(error, "停止生成失败");
  }
}

async function finishCurrentTopic() {
  if (!activeSessionId.value || sending.value || finishingTopic.value) return;
  finishingTopic.value = true;
  try {
    const result = await api.finishTopic(activeSessionId.value);
    showToast(result.growthProfileEnabled ? "本主题已沉淀，并更新了你的成长档案" : "本主题已沉淀");
    await refreshSessionList();
    await refreshProfile();
  } catch (error) {
    handleError(error, "主题沉淀失败");
  } finally {
    finishingTopic.value = false;
  }
}

async function generateHelpCard() {
  if (!activeSessionId.value || sending.value || generatingHelpCard.value) return;
  generatingHelpCard.value = true;
  try {
    const result = await api.generateHelpCard(activeSessionId.value);
    helpCard.value = result;
    helpCardContent.value = result.content;
    helpCardDialogOpen.value = true;
    showToast("求助卡已生成，可编辑后复制给老师");
    await refreshProfile();
  } catch (error) {
    handleError(error, "求助卡生成失败");
  } finally {
    generatingHelpCard.value = false;
  }
}

async function copyHelpCard() {
  if (!helpCardContent.value.trim()) {
    showToast("求助卡内容为空");
    return;
  }
  try {
    await copyText(helpCardContent.value);
    if (helpCard.value) {
      helpCard.value = await api.markHelpCardCopied(helpCard.value.id);
    }
    showToast("已复制，可粘贴给老师或班级群");
  } catch (error) {
    handleError(error, "复制失败，请手动选择文本复制");
  }
}

async function generateShareDraft() {
  if (!activeSessionId.value || sending.value || generatingShareDraft.value) return;
  generatingShareDraft.value = true;
  try {
    const result = await api.generateShareDraft(activeSessionId.value);
    shareDraft.value = result;
    shareDraftContent.value = result.content;
    shareDraftDialogOpen.value = true;
    showToast("分享稿已生成，可编辑后复制");
    await refreshProfile();
  } catch (error) {
    handleError(error, "分享稿生成失败");
  } finally {
    generatingShareDraft.value = false;
  }
}

async function copyShareDraft() {
  if (!shareDraftContent.value.trim()) {
    showToast("分享稿内容为空");
    return;
  }
  try {
    await copyText(shareDraftContent.value);
    if (shareDraft.value) {
      shareDraft.value = await api.markShareDraftCopied(shareDraft.value.id);
    }
    showToast("已复制，可粘贴到班级群");
  } catch (error) {
    handleError(error, "复制失败，请手动选择文本复制");
  }
}

async function openGrowthProfile() {
  profileDialogOpen.value = true;
  profileLoading.value = true;
  try {
    const [profileResult, helpCards, shareDrafts, reports] = await Promise.all([
      api.growthProfile(),
      api.helpCards(10),
      api.shareDrafts(10),
      api.periodicReports(10),
    ]);
    growthProfile.value = profileResult;
    helpCardHistory.value = helpCards;
    shareDraftHistory.value = shareDrafts;
    reportHistory.value = reports;
  } catch (error) {
    handleError(error, "成长档案加载失败");
  } finally {
    profileLoading.value = false;
  }
}

async function renameSession(sessionId: number, title: string, done: (success: boolean) => void) {
  if (sessionOperationPending.value) return;
  sessionOperationPending.value = true;
  try {
    await api.renameSession(sessionId, title);
    await refreshSessionList();
    showToast("会话名称已更新");
    done(true);
  } catch (error) {
    done(false);
    handleError(error, "会话改名失败");
  } finally {
    sessionOperationPending.value = false;
  }
}

async function deleteSession(sessionId: number, done: (success: boolean) => void) {
  if (sessionOperationPending.value) return;
  sessionOperationPending.value = true;
  try {
    await api.deleteSession(sessionId);
    sessions.value = await api.listSessions();
    done(true);
    showToast("会话已删除");
    if (activeSessionId.value === sessionId) {
      activeSessionId.value = null;
      messages.value = [];
      const nextSession = sessions.value[0];
      if (nextSession) await selectSession(nextSession.id, true);
      else {
        sessionOperationPending.value = false;
        await createSession();
      }
    }
  } catch (error) {
    done(false);
    handleError(error, "会话删除失败");
  } finally {
    sessionOperationPending.value = false;
  }
}

async function confirmLogout() {
  sessionOperationPending.value = true;
  try {
    await api.logout();
  } catch {
    // 本地登录态必须可退出，服务端失败不阻塞用户操作。
  } finally {
    sessionOperationPending.value = false;
    logoutDialogOpen.value = false;
    clearUserState();
  }
}

async function refreshSessionList() {
  sessions.value = await api.listSessions();
}

async function refreshProfile() {
  user.value = await api.profile();
}

function toUiMessage(message: ApiMessage): DisplayMessage {
  return {
    id: String(message.id),
    role: message.role,
    content: message.content,
    showReasoning: message.role === "assistant" && /<think(?:\s[^>]*)?>/i.test(message.content),
    createdAt: message.created_at,
    streaming: message.message_status === "GENERATING",
  };
}

function handleError(error: unknown, fallback: string) {
  if (error instanceof ApiError && error.status === 401) {
    clearUserState();
    showToast("登录状态已失效，请重新登录");
    return;
  }
  showToast(error instanceof Error ? error.message : fallback);
}

function clearUserState() {
  clearToken();
  user.value = null;
  sessions.value = [];
  messages.value = [];
  activeSessionId.value = null;
  statusText.value = "请先登录";
}

function showToast(message: string) {
  toastText.value = message;
  if (toastTimer) window.clearTimeout(toastTimer);
  toastTimer = window.setTimeout(() => {
    toastText.value = "";
    toastTimer = null;
  }, 3200);
}

async function copyText(text: string) {
  if (navigator.clipboard?.writeText) {
    await navigator.clipboard.writeText(text);
    return;
  }
  const textarea = document.createElement("textarea");
  textarea.value = text;
  textarea.setAttribute("readonly", "true");
  textarea.style.position = "fixed";
  textarea.style.left = "-9999px";
  document.body.appendChild(textarea);
  textarea.select();
  document.execCommand("copy");
  document.body.removeChild(textarea);
}
</script>

<template>
  <main class="app-shell">
    <section class="phone-frame" :class="{ 'login-mode': !user && !booting, 'chat-mode': Boolean(user) }">
      <div v-if="booting" class="booting">正在启动...</div>
      <LoginPanel v-else-if="!user" @logged-in="onLoggedIn" />
      <template v-else>
        <ChatHeader :user="user" :status-text="statusText" @open-history="drawerOpen = true" @logout="logoutDialogOpen = true" />
        <SessionQuota
          :used="user.todayUsed"
          :limit="user.dailyLimit"
          :entitlement="user.entitlement"
          :finishing="finishingTopic"
          :generating-help-card="generatingHelpCard"
          :generating-share-draft="generatingShareDraft"
          @finish-topic="finishCurrentTopic"
          @open-profile="openGrowthProfile"
          @generate-help-card="generateHelpCard"
          @generate-share-draft="generateShareDraft"
        />
        <MessageList
          ref="messageList"
          :messages="messages"
          :loading-session="loadingSession"
          @follow-change="followingOutput = $event"
        />
        <ChatComposer :loading="sending" :disabled="!activeSessionId || loadingSession" @send="send" @stop="stop" />
        <SessionDrawer
          :open="drawerOpen"
          :sessions="sessions"
          :active-session-id="activeSessionId"
          :loading="loadingSessions"
          :operation-pending="sessionOperationPending"
          @close="drawerOpen = false"
          @select="selectSession"
          @create="createSession"
          @rename="renameSession"
          @delete="deleteSession"
        />
      </template>
    </section>

    <div v-if="toastText" class="chat-toast" role="status">{{ toastText }}</div>

    <AppDialog v-if="profileDialogOpen" title="我的实修档案" labelled-by="growth-profile-title" @close="profileDialogOpen = false">
      <section class="growth-profile-dialog" :aria-busy="profileLoading">
        <p v-if="profileLoading" class="profile-empty">正在加载成长档案...</p>
        <template v-else-if="growthProfile?.profile">
          <pre>{{ growthProfile.profile.profileText }}</pre>
          <div class="growth-profile-fields">
            <p v-if="growthProfile.profile.recurringTopics"><strong>反复议题</strong>{{ growthProfile.profile.recurringTopics }}</p>
            <p v-if="growthProfile.profile.commonEmotions"><strong>常见情绪</strong>{{ growthProfile.profile.commonEmotions }}</p>
            <p v-if="growthProfile.profile.bodyPatterns"><strong>身体感受</strong>{{ growthProfile.profile.bodyPatterns }}</p>
            <p v-if="growthProfile.profile.homeworkDone"><strong>做过的功课</strong>{{ growthProfile.profile.homeworkDone }}</p>
            <p v-if="growthProfile.profile.recentProgress"><strong>最近进展</strong>{{ growthProfile.profile.recentProgress }}</p>
          </div>
        </template>
        <p v-else class="profile-empty">还没有成长档案。你可以在完成一次主题对话后点击“沉淀本主题”。</p>
        <div v-if="growthProfile?.recentSummaries.length" class="recent-topic-summaries">
          <h3>最近主题沉淀</h3>
          <article v-for="item in growthProfile.recentSummaries" :key="item.id">
            <time>{{ item.generatedAt }}</time>
            <p>{{ item.summary }}</p>
          </article>
        </div>
        <div v-if="reportHistory.length" class="recent-topic-summaries periodic-report-list">
          <h3>周期实修报告</h3>
          <article v-for="item in reportHistory" :key="`report-${item.id}`">
            <time>{{ item.reportTypeLabel }} · {{ item.periodStart }} 至 {{ item.periodEnd }} · {{ item.generatedAt }}</time>
            <p class="report-title">{{ item.title }}</p>
            <pre>{{ item.content }}</pre>
          </article>
        </div>
        <div v-if="helpCardHistory.length" class="recent-topic-summaries">
          <h3>最近老师求助卡</h3>
          <article v-for="item in helpCardHistory" :key="`help-${item.id}`">
            <time>{{ item.createdAt }} · {{ item.copied ? '已复制' : '未复制' }}</time>
            <p>{{ item.content.slice(0, 140) }}{{ item.content.length > 140 ? '…' : '' }}</p>
          </article>
        </div>
        <div v-if="shareDraftHistory.length" class="recent-topic-summaries">
          <h3>最近班级分享稿</h3>
          <article v-for="item in shareDraftHistory" :key="`share-${item.id}`">
            <time>{{ item.createdAt }} · {{ item.copied ? '已复制' : '未复制' }}</time>
            <p>{{ item.content.slice(0, 140) }}{{ item.content.length > 140 ? '…' : '' }}</p>
          </article>
        </div>
      </section>
    </AppDialog>

    <AppDialog v-if="helpCardDialogOpen" title="老师求助卡" labelled-by="help-card-title" @close="helpCardDialogOpen = false">
      <section class="help-card-dialog">
        <p>这不是转人工工单，系统不会自动发送给老师。你可以按真实情况删改后，自行复制给老师或班级群确认。</p>
        <textarea v-model="helpCardContent" aria-label="求助卡内容" />
      </section>
      <template #footer>
        <div class="dialog-actions">
          <button type="button" class="dialog-secondary" @click="helpCardDialogOpen = false">关闭</button>
          <button type="button" class="dialog-primary" @click="copyHelpCard">复制求助卡</button>
        </div>
      </template>
    </AppDialog>

    <AppDialog v-if="shareDraftDialogOpen" title="班级分享稿" labelled-by="share-draft-title" @close="shareDraftDialogOpen = false">
      <section class="help-card-dialog">
        <p>这只是分享草稿，系统不会自动发送到任何群。请删掉不想公开的隐私内容，并按自己的真实状态修改后再复制。</p>
        <textarea v-model="shareDraftContent" aria-label="班级分享稿内容" />
      </section>
      <template #footer>
        <div class="dialog-actions">
          <button type="button" class="dialog-secondary" @click="shareDraftDialogOpen = false">关闭</button>
          <button type="button" class="dialog-primary" @click="copyShareDraft">复制分享稿</button>
        </div>
      </template>
    </AppDialog>

    <AppDialog v-if="logoutDialogOpen" title="退出登录" labelled-by="logout-dialog-title" @close="logoutDialogOpen = false">
      <p class="confirm-copy">确定退出当前账号吗？</p>
      <template #footer>
        <div class="dialog-actions">
          <button type="button" class="dialog-secondary" :disabled="sessionOperationPending" @click="logoutDialogOpen = false">取消</button>
          <button type="button" class="dialog-primary" :disabled="sessionOperationPending" @click="confirmLogout">
            {{ sessionOperationPending ? "退出中..." : "退出" }}
          </button>
        </div>
      </template>
    </AppDialog>
  </main>
</template>
