"use client";

/**
 * 通知中心页面（ai14，P5）
 *
 * - 消费 useMyNotifications 查询（first 控制分页大小）
 * - 通知类型图标（作业/考试/成绩/系统）
 * - 已读/未读状态、"全部标记已读"按钮（useMarkAllAsRead）
 * - 单条"标记已读"（useMarkAsRead）
 * - WebSocket 实时更新（useNotificationsWebSocket 派发 CustomEvent）
 * - 加载更多：增大 first 参数（urql 自动重新查询）
 */

import { useCallback, useEffect, 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";
}

const TYPE_LABEL: Record<NotificationType, string> = {
  homework: "作业",
  exam: "考试",
  grade: "成绩",
  system: "系统",
};

function formatTime(iso: string): string {
  try {
    return new Date(iso).toLocaleString("zh-CN", { hour12: false });
  } catch {
    return iso;
  }
}

export default function NotificationsPage(): React.ReactNode {
  const { isConnected } = useNotificationsWebSocket();
  const [pageSize, setPageSize] = useState<number>(PAGE_SIZE);
  const [toast, setToast] = useState<ToastState | null>(null);

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

  const items: 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 结果（urql 无 onSuccess/onError 回调）
  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 hasMore = items.length < totalCount;

  return (
    <main
      className="px-md py-lg max-w-4xl mx-auto"
      aria-labelledby="notifications-title"
    >
      <header className="mb-lg">
        <div className="flex items-center justify-between gap-md">
          <div>
            <h1
              id="notifications-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>

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

      {fetching && items.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>
      ) : items.length === 0 ? (
        <p className="text-sm text-ink-muted" role="status">
          暂无通知
        </p>
      ) : (
        <ul className="space-y-md" aria-label="通知列表">
          {items.map((item) => (
            <li key={item.id}>
              <article
                className="card-paper p-md"
                aria-current={item.read ? "false" : "true"}
                aria-label={`${TYPE_LABEL[item.type]}通知：${item.title}`}
              >
                <div className="flex items-start justify-between gap-md">
                  <div className="min-w-0 flex-1">
                    <div className="flex items-center gap-sm mb-xs">
                      <span
                        className={`badge ${
                          item.type === "grade"
                            ? "badge-success"
                            : item.type === "exam"
                              ? "badge-warning"
                              : item.type === "homework"
                                ? "badge-info"
                                : "badge-danger"
                        }`}
                        aria-label={`类型：${TYPE_LABEL[item.type]}`}
                      >
                        {TYPE_LABEL[item.type]}
                      </span>
                      {!item.read ? (
                        <span className="badge badge-info" aria-label="未读">
                          未读
                        </span>
                      ) : null}
                    </div>
                    <h2 className="font-serif text-base text-ink text-truncate">
                      {item.title}
                    </h2>
                    {item.content ? (
                      <p className="mt-xs text-sm text-ink-muted text-clamp-2">
                        {item.content}
                      </p>
                    ) : null}
                    <p className="mt-xs text-xs text-ink-subtle">
                      {formatTime(item.createdAt)}
                    </p>
                  </div>
                  {!item.read ? (
                    <button
                      type="button"
                      className="btn-secondary shrink-0"
                      onClick={() => handleMarkRead(item.id)}
                      disabled={markReadMutation.fetching}
                      aria-label={`标记已读：${item.title}`}
                    >
                      标记已读
                    </button>
                  ) : null}
                </div>
              </article>
            </li>
          ))}
        </ul>
      )}

      {hasMore ? (
        <div className="mt-lg text-center">
          <button
            type="button"
            className="btn-secondary"
            onClick={handleLoadMore}
            disabled={fetching}
          >
            {fetching ? "加载中…" : "加载更多"}
          </button>
        </div>
      ) : items.length > 0 ? (
        <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>
  );
}
