// 通知偏好 Zod schema 单测
// 依据：02-architecture-design.md §14 通知偏好数据模型、project_rules §3.8 输入验证
// 覆盖：合法输入 / 缺失必填字段 / 非法 channel / 非法 event / 常量导出

import { describe, it, expect } from "vitest";
import {
  notificationPreferencesSchema,
  NOTIFICATION_EVENT_TYPES,
  NOTIFICATION_CHANNELS,
} from "./notification-preferences";

const VALID_INPUT = {
  parentId: "parent-001",
  preferences: {
    "student-001": {
      grade_recorded: { in_app: true, push: true },
      homework_graded: { in_app: true },
    },
  },
  defaults: {
    grade_recorded: { in_app: true, push: true },
    homework_graded: { in_app: true },
    homework_assigned: { in_app: true },
    exam_published: { in_app: true },
    attendance_alert: { in_app: true },
    school_announcement: { in_app: true },
    teacher_message: { in_app: true },
    fee_reminder: { in_app: true },
    event_invitation: { in_app: true },
  },
  updatedAt: "2026-07-13T00:00:00.000Z",
};

describe("notificationPreferencesSchema 合法输入", () => {
  it("合法输入通过校验", () => {
    const result = notificationPreferencesSchema.safeParse(VALID_INPUT);
    expect(result.success).toBe(true);
  });

  it("空 preferences 对象合法（新增家长无子女级偏好）", () => {
    const input = { ...VALID_INPUT, preferences: {} };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(true);
  });

  it("channelPref 所有字段可选（部分 channel 未设置合法）", () => {
    const input = {
      ...VALID_INPUT,
      defaults: {
        grade_recorded: {},
        homework_graded: { in_app: true },
        homework_assigned: {},
        exam_published: {},
        attendance_alert: {},
        school_announcement: {},
        teacher_message: {},
        fee_reminder: {},
        event_invitation: {},
      },
    };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(true);
  });

  it("boolean 值的 channel 偏好合法", () => {
    const input = {
      ...VALID_INPUT,
      defaults: {
        grade_recorded: {
          in_app: false,
          push: false,
          sms: true,
          email: false,
          wechat: true,
        },
        homework_graded: {},
        homework_assigned: {},
        exam_published: {},
        attendance_alert: {},
        school_announcement: {},
        teacher_message: {},
        fee_reminder: {},
        event_invitation: {},
      },
    };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(true);
  });
});

describe("notificationPreferencesSchema 缺失必填字段", () => {
  it("缺失 parentId 失败", () => {
    const { parentId: _omit, ...rest } = VALID_INPUT;
    const result = notificationPreferencesSchema.safeParse(rest);
    expect(result.success).toBe(false);
  });

  it("缺失 preferences 失败", () => {
    const { preferences: _omit, ...rest } = VALID_INPUT;
    const result = notificationPreferencesSchema.safeParse(rest);
    expect(result.success).toBe(false);
  });

  it("缺失 defaults 失败", () => {
    const { defaults: _omit, ...rest } = VALID_INPUT;
    const result = notificationPreferencesSchema.safeParse(rest);
    expect(result.success).toBe(false);
  });

  it("缺失 updatedAt 失败", () => {
    const { updatedAt: _omit, ...rest } = VALID_INPUT;
    const result = notificationPreferencesSchema.safeParse(rest);
    expect(result.success).toBe(false);
  });
});

describe("notificationPreferencesSchema 非法值", () => {
  it("parentId 为非字符串失败", () => {
    const input = { ...VALID_INPUT, parentId: 123 };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(false);
  });

  it("updatedAt 为非字符串失败", () => {
    const input = { ...VALID_INPUT, updatedAt: 12345 };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(false);
  });

  it("channel 偏好值为非 boolean 失败", () => {
    const input = {
      ...VALID_INPUT,
      defaults: {
        ...VALID_INPUT.defaults,
        grade_recorded: { in_app: "yes" },
      },
    };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(false);
  });

  it("eventType key 不在 NOTIFICATION_EVENT_TYPES 中失败", () => {
    const input = {
      ...VALID_INPUT,
      defaults: {
        ...VALID_INPUT.defaults,
        invalid_event_type: { in_app: true },
      },
    };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(false);
  });

  it("channel key 不在 NOTIFICATION_CHANNELS 中被静默剥离（.partial() 行为）", () => {
    const input = {
      ...VALID_INPUT,
      defaults: {
        ...VALID_INPUT.defaults,
        grade_recorded: {
          ...VALID_INPUT.defaults.grade_recorded,
          telegram: true,
        },
      },
    };
    const result = notificationPreferencesSchema.safeParse(input);
    // Zod .partial() 默认剥离未知 key（非 .strict()），故 parse 成功
    expect(result.success).toBe(true);
    if (result.success) {
      // telegram key 被剥离，不在输出中
      const gradeRecorded = result.data.defaults.grade_recorded;
      expect(gradeRecorded).not.toHaveProperty("telegram");
    }
  });

  it("preferences 的 childId key 可以是任意字符串（不限于固定列表）", () => {
    const input = {
      ...VALID_INPUT,
      preferences: {
        "any-custom-id": {
          grade_recorded: { in_app: true },
        },
      },
    };
    const result = notificationPreferencesSchema.safeParse(input);
    expect(result.success).toBe(true);
  });
});

describe("NOTIFICATION_EVENT_TYPES 常量", () => {
  it("包含 9 种事件类型", () => {
    expect(NOTIFICATION_EVENT_TYPES).toHaveLength(9);
  });

  it("包含所有预期事件类型", () => {
    expect(NOTIFICATION_EVENT_TYPES).toContain("grade_recorded");
    expect(NOTIFICATION_EVENT_TYPES).toContain("homework_graded");
    expect(NOTIFICATION_EVENT_TYPES).toContain("homework_assigned");
    expect(NOTIFICATION_EVENT_TYPES).toContain("exam_published");
    expect(NOTIFICATION_EVENT_TYPES).toContain("attendance_alert");
    expect(NOTIFICATION_EVENT_TYPES).toContain("school_announcement");
    expect(NOTIFICATION_EVENT_TYPES).toContain("teacher_message");
    expect(NOTIFICATION_EVENT_TYPES).toContain("fee_reminder");
    expect(NOTIFICATION_EVENT_TYPES).toContain("event_invitation");
  });
});

describe("NOTIFICATION_CHANNELS 常量", () => {
  it("包含 5 种渠道", () => {
    expect(NOTIFICATION_CHANNELS).toHaveLength(5);
  });

  it("包含所有预期渠道", () => {
    expect(NOTIFICATION_CHANNELS).toContain("in_app");
    expect(NOTIFICATION_CHANNELS).toContain("push");
    expect(NOTIFICATION_CHANNELS).toContain("sms");
    expect(NOTIFICATION_CHANNELS).toContain("email");
    expect(NOTIFICATION_CHANNELS).toContain("wechat");
  });
});
