import type { MsgClient } from "../../clients/msg.client.js";
import type { NotificationDto } from "../../clients/dtos.js";
import type { NotificationItem, NotificationType } from "../types.js";
import type { GraphqlContext } from "../context.js";
import { CacheKeys } from "../../shared/cache/cache-key.builder.js";
import {
  withCacheFallback,
  invalidateCache,
} from "../../aggregation/fallback-strategy.js";
import { env } from "../../config/env.js";
import { logger } from "../../shared/observability/logger.js";

const NOTIFICATION_TYPE_MAP: Record<string, NotificationType> = {
  SYSTEM: "SYSTEM",
  EXAM: "EXAM",
  HOMEWORK: "HOMEWORK",
  GRADE: "GRADE",
  ATTENDANCE: "ATTENDANCE",
  ANNOUNCEMENT: "ANNOUNCEMENT",
};

/**
 * DTO → GraphQL Type 映射。
 *
 * createdAt: proto int64（epoch ms）→ ISO 8601 字符串（DateTime scalar）
 */
function mapNotification(dto: NotificationDto): NotificationItem {
  return {
    id: dto.id,
    type: NOTIFICATION_TYPE_MAP[dto.type] ?? "SYSTEM",
    title: dto.title,
    content: dto.content,
    read: dto.isRead,
    childId: dto.childId ?? null,
    createdAt: new Date(dto.createdAt).toISOString(),
  };
}

const MAX_NOTIFICATION_PAGE_SIZE = 50;

/**
 * notifications Query resolver。
 *
 * 支持分页 + unreadOnly 过滤，Redis 缓存 15s。
 */
export function buildNotificationsQueryResolver(deps: {
  msgClient: MsgClient;
}) {
  return async (
    _parent: unknown,
    args: { unreadOnly?: boolean; page?: number; pageSize?: number },
    ctx: GraphqlContext,
  ): Promise<NotificationItem[]> => {
    const parentId = ctx.session.parentId;
    const unreadOnly = args.unreadOnly ?? false;
    const page = Math.max(1, args.page ?? 1);
    const pageSize = Math.min(
      MAX_NOTIFICATION_PAGE_SIZE,
      Math.max(1, args.pageSize ?? 20),
    );

    const cacheKey = CacheKeys.notifications(parentId, page);
    const result = await withCacheFallback(
      cacheKey,
      () => deps.msgClient.listNotifications(parentId, unreadOnly),
      env.GRADES_CACHE_TTL_SECONDS,
      "notifications",
    );

    const dtos = result.data ?? [];
    let items = dtos.map(mapNotification);

    // 二次过滤（unreadOnly 在缓存层可能不精确）
    if (unreadOnly) {
      items = items.filter((n) => !n.read);
    }

    // 分页
    const start = (page - 1) * pageSize;
    return items.slice(start, start + pageSize);
  };
}

/**
 * markNotificationRead Mutation resolver。
 *
 * 调用 msg.MarkAsRead 后失效通知缓存。
 */
export function buildMarkNotificationReadMutationResolver(deps: {
  msgClient: MsgClient;
}) {
  return async (
    _parent: unknown,
    args: { notificationId: string },
    ctx: GraphqlContext,
  ): Promise<NotificationItem> => {
    const parentId = ctx.session.parentId;

    await deps.msgClient.markAsRead(args.notificationId);

    // 失效通知缓存（所有页）
    await invalidateCache(CacheKeys.notifications(parentId, 1));

    logger.info(
      { parentId, notificationId: args.notificationId },
      "Notification marked as read",
    );

    // 返回更新后的通知（从缓存或下游重新获取）
    const all = await deps.msgClient.listNotifications(parentId, false);
    const updated = all.find((n) => n.id === args.notificationId);
    if (!updated) {
      return {
        id: args.notificationId,
        type: "SYSTEM",
        title: "",
        content: "",
        read: true,
        childId: null,
        createdAt: new Date().toISOString(),
      };
    }
    return mapNotification(updated);
  };
}
