import type { MsgClient } from "../../clients/msg.client.js";
import type { NotificationPreferencesDto } from "../../clients/dtos.js";
import type {
  NotificationPreferencesType,
  NotificationChannel,
} 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";
import {
  UpdateNotificationPreferencesSchema,
  type UpdateNotificationPreferencesInput,
} from "../../parent/dto/parent-inputs.dto.js";

const CHANNEL_MAP: Record<string, NotificationChannel> = {
  APP: "APP",
  SMS: "SMS",
  EMAIL: "EMAIL",
  WECHAT: "WECHAT",
};

/**
 * DTO → GraphQL Type 映射。
 */
function mapPreferences(
  dto: NotificationPreferencesDto,
): NotificationPreferencesType {
  return {
    channels: dto.channels.map((c) => CHANNEL_MAP[c] ?? "APP"),
    eventTypes: { ...dto.eventTypes },
  };
}

/**
 * notificationPreferences Query resolver。
 *
 * Redis 缓存 300s（PERMISSIONS_CACHE_TTL_SECONDS）。
 */
export function buildNotificationPreferencesQueryResolver(deps: {
  msgClient: MsgClient;
}) {
  return async (
    _parent: unknown,
    _args: unknown,
    ctx: GraphqlContext,
  ): Promise<NotificationPreferencesType> => {
    const parentId = ctx.session.parentId;
    const cacheKey = CacheKeys.notificationPrefs(parentId);

    const result = await withCacheFallback(
      cacheKey,
      () => deps.msgClient.getNotificationPreferences(parentId),
      env.PERMISSIONS_CACHE_TTL_SECONDS,
      "notification-prefs",
    );

    const dto = result.data;
    if (!dto) {
      // 下游失败且无缓存，返回默认偏好
      return {
        channels: ["APP"],
        eventTypes: {
          gradeReleased: true,
          homeworkGraded: true,
          examPublished: true,
          attendanceAlert: true,
          schoolAnnouncement: true,
        },
      };
    }
    return mapPreferences(dto);
  };
}

/**
 * updateNotificationPreferences Mutation resolver。
 *
 * Zod 校验输入 → 调 msg.UpdateNotificationPreferences → 失效缓存 → 返回新偏好。
 */
export function buildUpdateNotificationPreferencesMutationResolver(deps: {
  msgClient: MsgClient;
}) {
  return async (
    _parent: unknown,
    args: { input: UpdateNotificationPreferencesInput },
    ctx: GraphqlContext,
  ): Promise<NotificationPreferencesType> => {
    const parentId = ctx.session.parentId;

    // Zod 校验
    const parsed = UpdateNotificationPreferencesSchema.safeParse(args.input);
    if (!parsed.success) {
      throw new Error(
        `Invalid notification preferences input: ${JSON.stringify(
          parsed.error.flatten(),
        )}`,
      );
    }

    // 获取当前偏好（合并未传的 eventTypes）
    const current = await deps.msgClient.getNotificationPreferences(parentId);
    const merged: NotificationPreferencesDto = {
      parentId,
      channels: parsed.data.channels,
      eventTypes: {
        gradeReleased:
          parsed.data.eventTypes.gradeReleased ??
          current.eventTypes.gradeReleased,
        homeworkGraded:
          parsed.data.eventTypes.homeworkGraded ??
          current.eventTypes.homeworkGraded,
        examPublished:
          parsed.data.eventTypes.examPublished ??
          current.eventTypes.examPublished,
        attendanceAlert:
          parsed.data.eventTypes.attendanceAlert ??
          current.eventTypes.attendanceAlert,
        schoolAnnouncement:
          parsed.data.eventTypes.schoolAnnouncement ??
          current.eventTypes.schoolAnnouncement,
      },
    };

    const updated = await deps.msgClient.updateNotificationPreferences(
      parentId,
      merged,
    );

    // 失效偏好缓存
    await invalidateCache(CacheKeys.notificationPrefs(parentId));

    logger.info(
      { parentId, channels: updated.channels },
      "Notification preferences updated",
    );

    return mapPreferences(updated);
  };
}
