"use client";

/**
 * 消息中心页面（ai14，P5）
 *
 * 与 /notifications 的区别：
 * - /notifications 偏系统通知（卡片列表 + 类型徽标 + 单独标记按钮）
 * - /messages 偏师生互动消息（对话式列表 + 头像首字 + 展开详情 + 全部/未读筛选 Tab）
 *
 * - 消费 useMyNotifications 查询（first 控制分页大小）
 * - 全部 / 未读 筛选 Tab（客户端过滤）
 * - 消息项以"发送方"视角展示：类型首字头像 + 标题 + 时间 + 已读未读圆点
 * - 点击展开详情（手风琴式）
 * - 单条标记已读（useMarkAsRead）+ 全部标记已读（useMarkAllAsRead）
 * - WebSocket 实时更新（复用通知中心的 NOTIFICATION_UPDATED_EVENT）
 * - 加载更多：增大 first 参数
 */

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  useMarkAllAsRead,
  useMarkAsRead,
  useMyNotifications,
} from "@/lib/graphql/hooks";
import type { NotificationItem, NotificationType } from "@/lib/graphql/types";
import {
  NOTIFICATION_UPDATED_EVENT,
  useNotificationsWebSocket,
} from "@/hooks/use-notifications-websocket";

const PAGE_SIZE = 20;

interface ToastState {
  message: string;
  tone: "success" | "error";
}

type FilterTab = "all" | "unread";

/** 类型 → "发送方" 角色名（消息中心视角） */
const SENDER_LABEL: Record<NotificationType, string> = {
  homework: "学科老师",
  exam: "教务老师",
  grade: "教务老师",
  system: "系统通知",
};

/** 类型 → 头像首字 */
const SENDER_INITIAL: Record<NotificationType, string> = {
  homework: "师",
  exam: "教",
  grade: "教",
  system: "系",
};

/** 类型 → 头像背景色（语义令牌） */
function senderColor(type: NotificationType): string {
  switch (type) {
    case "grade":
      return "var(--color-success)";
    case "exam":
      return "var(--color-warning)";
    case "homework":
      return "var(--color-accent)";
    case "system":
      return "var(--color-ink-muted)";
  }
}

/** 相对时间格式化（"刚刚"/"3 分钟前"/"昨天"等） */
function formatRelativeTime(iso: string): string {
  try {
    const now = Date.now();
    const t = new Date(iso).getTime();
    const diff = Math.max(0, now - t);
    const minutes = Math.floor(diff / 60000);
    if (minutes < 1) return "刚刚";
    if (minutes < 60) return `${minutes} 分钟前`;
    const hours = Math.floor(minutes / 60);
    if (hours < 24) return `${hours} 小时前`;
    const days = Math.floor(hours / 24);
    if (days < 7) return `${days} 天前`;
    return new Date(iso).toLocaleDateString("zh-CN");
  } catch {
    return iso;
  }
}

/** 完整时间格式化（展开详情时显示） */
function formatFullTime(iso: string): string {
  try {
    return new Date(iso).toLocaleString("zh-CN", { hour12: false });
  } catch {
    return iso;
  }
}

export default function MessagesPage(): React.ReactNode {
  const { isConnected } = useNotificationsWebSocket();
  const [pageSize, setPageSize] = useState<number>(PAGE_SIZE);
  const [activeTab, setActiveTab] = useState<FilterTab>("all");
  const [expandedId, setExpandedId] = useState<string | null>(null);
  const [toast, setToast] = useState<ToastState | null>(null);

  const { data, fetching, error, reexecute } = useMyNotifications(pageSize);

  const allItems: NotificationItem[] = data?.items ?? [];
  const totalCount = data?.totalCount ?? 0;
  const unreadCount = data?.unreadCount ?? 0;

  const markReadMutation = useMarkAsRead();
  const markAllMutation = useMarkAllAsRead();

  const markingReadRef = useRef<string | null>(null);
  const markingAllRef = useRef(false);

  const showToast = useCallback(
    (message: string, tone: ToastState["tone"]): void => {
      setToast({ message, tone });
      window.setTimeout(() => setToast(null), 2500);
    },
    [],
  );

  // WebSocket 更新事件：重新查询
  useEffect(() => {
    const handler = (): void => {
      reexecute();
    };
    window.addEventListener(NOTIFICATION_UPDATED_EVENT, handler);
    return () => {
      window.removeEventListener(NOTIFICATION_UPDATED_EVENT, handler);
    };
  }, [reexecute]);

  // 监听标记已读 mutation 结果
  useEffect(() => {
    if (!markingReadRef.current) return;
    if (markReadMutation.fetching) return;

    markingReadRef.current = null;

    if (markReadMutation.success) {
      showToast("已标记为已读", "success");
      reexecute();
    } else {
      const message =
        markReadMutation.errors[0]?.message ??
        markReadMutation.error?.message ??
        "标记失败，请重试";
      showToast(message, "error");
    }
  }, [
    markReadMutation.fetching,
    markReadMutation.success,
    markReadMutation.error,
    markReadMutation.errors,
    reexecute,
    showToast,
  ]);

  // 监听批量标记已读 mutation 结果
  useEffect(() => {
    if (!markingAllRef.current) return;
    if (markAllMutation.fetching) return;

    markingAllRef.current = false;

    if (markAllMutation.success) {
      showToast("已全部标记为已读", "success");
      reexecute();
    } else {
      const message =
        markAllMutation.errors[0]?.message ??
        markAllMutation.error?.message ??
        "操作失败，请重试";
      showToast(message, "error");
    }
  }, [
    markAllMutation.fetching,
    markAllMutation.success,
    markAllMutation.error,
    markAllMutation.errors,
    reexecute,
    showToast,
  ]);

  const handleMarkRead = useCallback(
    (id: string): void => {
      markingReadRef.current = id;
      markReadMutation.execute({ notificationId: id });
    },
    [markReadMutation],
  );

  const handleMarkAllRead = useCallback((): void => {
    markingAllRef.current = true;
    markAllMutation.execute({});
  }, [markAllMutation]);

  const handleLoadMore = useCallback((): void => {
    setPageSize((prev) => prev + PAGE_SIZE);
  }, []);

  const handleToggleExpand = useCallback((id: string): void => {
    setExpandedId((prev) => (prev === id ? null : id));
  }, []);

  // 客户端过滤（全部 / 未读）
  const filteredItems = useMemo(() => {
    if (activeTab === "unread") {
      return allItems.filter((it) => !it.read);
    }
    return allItems;
  }, [allItems, activeTab]);

  const hasMore = allItems.length < totalCount;

  return (
    <main
      className="px-md py-lg max-w-3xl mx-auto"
      aria-labelledby="messages-title"
    >
      <header className="mb-lg">
        <div className="flex items-center justify-between gap-md">
          <div className="min-w-0 flex-1">
            <h1
              id="messages-title"
              className="font-serif text-2xl text-ink"
            >
              消息中心
            </h1>
            <p className="mt-xs text-sm text-ink-muted">
              师生互动消息与教学反馈
            </p>
          </div>
          <button
            type="button"
            className="btn-secondary"
            onClick={handleMarkAllRead}
            disabled={unreadCount === 0 || markAllMutation.fetching}
            aria-label="全部标记已读"
          >
            全部已读
            {unreadCount > 0 ? `（${unreadCount}）` : ""}
          </button>
        </div>
        <p
          className="mt-sm text-xs"
          style={{
            color: isConnected
              ? "var(--color-success)"
              : "var(--color-warning)",
          }}
          role="status"
          aria-live="polite"
        >
          {isConnected ? "● 实时连接已建立" : "○ 实时连接已断开，正在重连…"}
        </p>
      </header>

      {/* 筛选 Tab */}
      <div
        className="flex gap-xs mb-lg"
        role="tablist"
        aria-label="消息筛选"
      >
        <FilterTabButton
          active={activeTab === "all"}
          onClick={() => setActiveTab("all")}
          label="全部"
          count={totalCount}
        />
        <FilterTabButton
          active={activeTab === "unread"}
          onClick={() => setActiveTab("unread")}
          label="未读"
          count={unreadCount}
        />
      </div>

      <div className="rule-thin mb-lg" />

      {fetching && allItems.length === 0 ? (
        <p className="text-sm text-ink-muted" aria-live="polite">
          加载中…
        </p>
      ) : error ? (
        <div className="mark-left py-md px-md mb-md" aria-live="assertive">
          <p className="text-sm text-ink">{error.message || "加载失败"}</p>
          <button
            type="button"
            className="btn-secondary mt-sm"
            onClick={() => reexecute()}
          >
            重试
          </button>
        </div>
      ) : filteredItems.length === 0 ? (
        <p className="text-sm text-ink-muted" role="status">
          {activeTab === "unread" ? "暂无未读消息" : "暂无消息"}
        </p>
      ) : (
        <ul className="space-y-sm" aria-label="消息列表">
          {filteredItems.map((item) => (
            <MessageRow
              key={item.id}
              item={item}
              expanded={expandedId === item.id}
              onToggle={() => handleToggleExpand(item.id)}
              onMarkRead={() => handleMarkRead(item.id)}
              markingRead={
                markingReadRef.current === item.id && markReadMutation.fetching
              }
            />
          ))}
        </ul>
      )}

      {hasMore && activeTab === "all" ? (
        <div className="mt-lg text-center">
          <button
            type="button"
            className="btn-secondary"
            onClick={handleLoadMore}
            disabled={fetching}
          >
            {fetching ? "加载中…" : "加载更多"}
          </button>
        </div>
      ) : filteredItems.length > 0 && activeTab === "all" ? (
        <p className="mt-lg text-center text-xs text-ink-subtle">
          已显示全部 {totalCount} 条消息
        </p>
      ) : null}

      {toast ? (
        <div
          className="fixed bottom-md left-1/2 -translate-x-1/2 card-paper px-lg py-md text-sm"
          role="alert"
          aria-live="assertive"
          style={{
            color:
              toast.tone === "success"
                ? "var(--color-success)"
                : "var(--color-danger)",
          }}
        >
          {toast.message}
        </div>
      ) : null}
    </main>
  );
}

/** 筛选 Tab 按钮 */
function FilterTabButton({
  active,
  onClick,
  label,
  count,
}: {
  active: boolean;
  onClick: () => void;
  label: string;
  count: number;
}): JSX.Element {
  return (
    <button
      type="button"
      role="tab"
      aria-selected={active}
      onClick={onClick}
      className="px-md py-sm text-sm rounded-sm transition-colors motion-safe"
      style={{
        background: active ? "var(--color-accent-muted)" : "transparent",
        color: active ? "var(--color-accent)" : "var(--color-ink-muted)",
        borderBottom: active
          ? "2px solid var(--color-accent)"
          : "2px solid transparent",
        fontFamily: active ? "var(--font-serif)" : "var(--font-sans)",
      }}
    >
      {label}
      {count > 0 ? (
        <span
          className="ml-xs text-xs"
          aria-label={`${label} ${count} 条`}
        >
          ({count})
        </span>
      ) : null}
    </button>
  );
}

/** 单条消息行（对话式） */
function MessageRow({
  item,
  expanded,
  onToggle,
  onMarkRead,
  markingRead,
}: {
  item: NotificationItem;
  expanded: boolean;
  onToggle: () => void;
  onMarkRead: () => void;
  markingRead: boolean;
}): JSX.Element {
  const initial = SENDER_INITIAL[item.type];
  const sender = SENDER_LABEL[item.type];
  const color = senderColor(item.type);

  return (
    <li>
      <article
        className="card-paper overflow-hidden"
        aria-current={item.read ? "false" : "true"}
        aria-label={`${sender}：${item.title}`}
      >
        <button
          type="button"
          onClick={onToggle}
          className="w-full text-left p-md flex items-start gap-md"
          aria-expanded={expanded}
          aria-controls={`msg-detail-${item.id}`}
        >
          {/* 头像（类型首字） */}
          <span
            className="inline-flex w-10 h-10 flex-shrink-0 items-center justify-center rounded-full font-serif text-sm"
            style={{ background: color, color: "var(--color-paper)" }}
            aria-hidden="true"
          >
            {initial}
          </span>

          {/* 主体内容 */}
          <div className="min-w-0 flex-1">
            <div className="flex items-baseline justify-between gap-sm">
              <p
                className="text-xs"
                style={{ color: "var(--color-ink-muted)" }}
              >
                {sender}
              </p>
              <p
                className="text-xs font-mono flex-shrink-0"
                style={{ color: "var(--color-ink-subtle)" }}
              >
                {formatRelativeTime(item.createdAt)}
              </p>
            </div>
            <p
              className="mt-xs text-sm font-medium text-truncate"
              style={{
                color: item.read ? "var(--color-ink-muted)" : "var(--color-ink)",
                fontFamily: item.read
                  ? "var(--font-sans)"
                  : "var(--font-serif)",
              }}
            >
              {item.title}
            </p>
            {!expanded && item.content ? (
              <p
                className="mt-xs text-xs text-clamp-1"
                style={{ color: "var(--color-ink-subtle)" }}
              >
                {item.content}
              </p>
            ) : null}
          </div>

          {/* 未读圆点 */}
          {!item.read ? (
            <span
              className="mt-xs inline-block w-2 h-2 rounded-full flex-shrink-0"
              style={{ background: "var(--color-accent)" }}
              aria-label="未读"
            />
          ) : null}
        </button>

        {/* 展开详情 */}
        {expanded ? (
          <div
            id={`msg-detail-${item.id}`}
            className="px-md pb-md pt-0"
            role="region"
            aria-label="消息详情"
          >
            <div
              className="ml-12 pl-md py-sm"
              style={{
                borderLeft: "2px solid var(--color-rule)",
              }}
            >
              {item.content ? (
                <p
                  className="text-sm"
                  style={{ color: "var(--color-ink)" }}
                >
                  {item.content}
                </p>
              ) : (
                <p
                  className="text-sm"
                  style={{ color: "var(--color-ink-subtle)" }}
                >
                  （无详细内容）
                </p>
              )}
              <p
                className="mt-sm text-xs font-mono"
                style={{ color: "var(--color-ink-subtle)" }}
              >
                {formatFullTime(item.createdAt)}
              </p>
              {!item.read ? (
                <button
                  type="button"
                  className="btn-secondary mt-sm text-xs"
                  onClick={onMarkRead}
                  disabled={markingRead}
                  aria-label={`标记已读：${item.title}`}
                >
                  {markingRead ? "标记中…" : "标记已读"}
                </button>
              ) : (
                <span
                  className="inline-block mt-sm text-xs"
                  style={{ color: "var(--color-ink-subtle)" }}
                >
                  ✓ 已读
                </span>
              )}
            </div>
          </div>
        ) : null}
      </article>
    </li>
  );
}
