import type { EventHandler, EventContext } from "./event-handler.js";
import type { MsgClient } from "../../../clients/msg.client.js";
import type { PushHttpClient } from "../../../clients/http/push-http.client.js";
import type { NotificationPreferencesDto } from "../../../clients/dtos.js";
import { CacheKeys } from "../../cache/cache-key.builder.js";
import { withCacheFallback } from "../../../aggregation/fallback-strategy.js";
import { env } from "../../../config/env.js";
import { logger } from "../../observability/logger.js";

/**
 * 通知推送事件处理器（P5.4 + P5.5，对齐 02-architecture-design.md §5.3 + §5.4）。
 *
 * 处理 edu.notification.sent + edu.teaching.* 事件，
 * 根据家长 NotificationPreferences 过滤后通过 push-gateway HTTP 推送。
 *
 * 通知偏好过滤逻辑（§5.4）：
 * 1. 拉取家长 NotificationPreferences（Redis 缓存 300s）
 * 2. 按 eventTypeMap 映射事件类型 → 偏好开关
 * 3. 若偏好开关为 false → 不推送
 * 4. 取 prefs.channels 与 event.channels 交集，无交集则不推送
 * 5. 调用 pushClient.pushViaHttp(parentId, payload, allowedChannels)
 */

interface NotificationEventBody {
  parentId: string;
  childId?: string;
  type: string;
  title: string;
  content: string;
  channels?: string[];
}

const EVENT_TYPE_MAP: Record<
  string,
  keyof NotificationPreferencesDto["eventTypes"]
> = {
  GRADE: "gradeReleased",
  HOMEWORK: "homeworkGraded",
  EXAM: "examPublished",
  ATTENDANCE: "attendanceAlert",
  ANNOUNCEMENT: "schoolAnnouncement",
};

const TEACHING_TOPIC_TYPE_MAP: Record<string, string> = {
  "edu.teaching.grade.recorded": "GRADE",
  "edu.teaching.homework.graded": "HOMEWORK",
  "edu.teaching.exam.published": "EXAM",
};

export class NotificationPushHandler implements EventHandler<NotificationEventBody> {
  constructor(
    private readonly msgClient: MsgClient,
    private readonly pushClient: PushHttpClient,
  ) {}

  async handle(ctx: EventContext<NotificationEventBody>): Promise<void> {
    const { topic, body } = ctx;

    // 教学事件映射为通知类型
    const notificationType =
      topic === "edu.notification.sent"
        ? body.type
        : (TEACHING_TOPIC_TYPE_MAP[topic] ?? "SYSTEM");

    // 教学事件构造通知内容
    const title = body.title || this.getDefaultTitle(notificationType);
    const content = body.content || "";

    // 拉取家长通知偏好（Redis 缓存 300s）
    const prefs = await this.getPreferences(body.parentId);

    // 偏好开关检查
    const prefKey = EVENT_TYPE_MAP[notificationType];
    if (prefKey && !prefs.eventTypes[prefKey]) {
      logger.info(
        { parentId: body.parentId, type: notificationType, prefKey },
        "Notification skipped: preference disabled",
      );
      return;
    }

    // 渠道交集检查
    const eventChannels = body.channels ?? ["APP"];
    const allowedChannels = prefs.channels.filter((c) =>
      eventChannels.includes(c),
    );

    if (allowedChannels.length === 0) {
      logger.info(
        {
          parentId: body.parentId,
          eventChannels,
          prefChannels: prefs.channels,
        },
        "Notification skipped: no channel intersection",
      );
      return;
    }

    // 调用 push-gateway HTTP 推送
    const result = await this.pushClient.pushViaHttp({
      parentId: body.parentId,
      title,
      content,
      channels: allowedChannels,
      childId: body.childId,
    });

    if (!result.success) {
      logger.error(
        { parentId: body.parentId, error: result.error },
        "Push-gateway delivery failed",
      );
    } else {
      logger.info(
        {
          parentId: body.parentId,
          messageId: result.messageId,
          channels: allowedChannels,
        },
        "Notification pushed via HTTP",
      );
    }
  }

  private async getPreferences(
    parentId: string,
  ): Promise<NotificationPreferencesDto> {
    const cacheKey = CacheKeys.notificationPrefs(parentId);
    const result = await withCacheFallback(
      cacheKey,
      () => this.msgClient.getNotificationPreferences(parentId),
      env.PERMISSIONS_CACHE_TTL_SECONDS,
      "notification-prefs",
    );

    if (result.data) {
      return result.data;
    }

    // 下游失败且无缓存，返回默认偏好（全部开启）
    return {
      parentId,
      channels: ["APP"],
      eventTypes: {
        gradeReleased: true,
        homeworkGraded: true,
        examPublished: true,
        attendanceAlert: true,
        schoolAnnouncement: true,
      },
    };
  }

  private getDefaultTitle(type: string): string {
    const titles: Record<string, string> = {
      GRADE: "成绩发布通知",
      HOMEWORK: "作业批改完成",
      EXAM: "考试发布通知",
      ATTENDANCE: "考勤提醒",
      ANNOUNCEMENT: "学校公告",
      SYSTEM: "系统通知",
    };
    return titles[type] ?? "新通知";
  }
}
