import { describe, it, expect, vi, beforeEach } from "vitest";

// Mock prisma + email before importing the runner.
const mockReminders: Array<{
  id: string;
  userId: string;
  title: string;
  reminderDate: Date;
  isLunar: boolean;
  recurrence: string | null;
  notified: boolean;
  user: { email: string; name: string };
}> = [];

const updateCalls: Array<{ id: string; data: { notified: boolean } }> = [];

vi.mock("@/lib/db", () => ({
  prisma: {
    reminder: {
      findMany: vi.fn(
        async ({ where, include }: { where?: { notified?: boolean }; include?: unknown }) => {
          const filtered = mockReminders.filter((r) => {
            if (where?.notified === false) return r.notified === false;
            if (where?.notified === true) return r.notified === true;
            return true;
          });
          if (include) return filtered;
          return filtered.map(({ user: _user, ...rest }) => rest);
        }
      ),
      update: vi.fn(
        async ({ where, data }: { where: { id: string }; data: { notified: boolean } }) => {
          updateCalls.push({ id: where.id, data });
          const r = mockReminders.find((x) => x.id === where.id);
          if (r) r.notified = data.notified;
          return r;
        }
      ),
    },
  },
}));

const sendCalls: Array<{ to: string | string[]; subject: string }> = [];
vi.mock("@/lib/email/provider", () => ({
  sendEmail: vi.fn(async (input: { to: string | string[]; subject: string }) => {
    sendCalls.push({ to: input.to, subject: input.subject });
    return { ok: true, id: "test_send_id" };
  }),
}));

import { runReminders } from "./runner";

beforeEach(() => {
  mockReminders.length = 0;
  updateCalls.length = 0;
  sendCalls.length = 0;
});

describe("reminder runner", () => {
  it("fires a one-shot solar reminder due today", async () => {
    const today = new Date(2026, 4, 30); // May 30 2026
    mockReminders.push({
      id: "r1",
      userId: "u1",
      title: "Sinh nhật",
      reminderDate: new Date(2026, 4, 30),
      isLunar: false,
      recurrence: null,
      notified: false,
      user: { email: "u@example.com", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.fired).toBe(1);
    expect(sendCalls).toHaveLength(1);
    expect(sendCalls[0].to).toBe("u@example.com");
    expect(updateCalls).toHaveLength(1);
    expect(updateCalls[0].data.notified).toBe(true);
  });

  it("skips a one-shot solar reminder for a future date", async () => {
    const today = new Date(2026, 4, 30);
    mockReminders.push({
      id: "r2",
      userId: "u1",
      title: "Future",
      reminderDate: new Date(2026, 5, 15), // 16 days out
      isLunar: false,
      recurrence: null,
      notified: false,
      user: { email: "u@example.com", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.fired).toBe(0);
    expect(r.skipped).toBe(1);
  });

  it("fires a yearly recurring reminder when its (month, day) matches today", async () => {
    const today = new Date(2027, 4, 30); // anniversary year
    mockReminders.push({
      id: "r3",
      userId: "u1",
      title: "Anniversary",
      reminderDate: new Date(2026, 4, 30), // original year earlier
      isLunar: false,
      recurrence: "yearly",
      notified: false,
      user: { email: "u@example.com", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.fired).toBe(1);
  });

  it("does not fire a yearly recurring reminder on a non-matching day", async () => {
    const today = new Date(2027, 4, 29);
    mockReminders.push({
      id: "r4",
      userId: "u1",
      title: "Anniversary",
      reminderDate: new Date(2026, 4, 30),
      isLunar: false,
      recurrence: "yearly",
      notified: false,
      user: { email: "u@example.com", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.fired).toBe(0);
  });

  it("fires a past-due one-shot reminder so users don't silently miss", async () => {
    const today = new Date(2026, 4, 30);
    mockReminders.push({
      id: "r5",
      userId: "u1",
      title: "Late",
      reminderDate: new Date(2026, 4, 25), // 5 days ago, not yet sent
      isLunar: false,
      recurrence: null,
      notified: false,
      user: { email: "u@example.com", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.fired).toBe(1);
  });

  it("rolls over recurring reminders so next cycle can fire again", async () => {
    const today = new Date(2026, 4, 30);
    // Sent yearly anniversary last year (2025), should be reset for 2026.
    mockReminders.push({
      id: "r6",
      userId: "u1",
      title: "Yearly",
      reminderDate: new Date(2025, 4, 30),
      isLunar: false,
      recurrence: "yearly",
      notified: true,
      user: { email: "u@example.com", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.rolled).toBe(1);
    // After rollover, this reminder fires today.
    expect(r.fired).toBe(1);
  });

  it("skips reminders whose user has no email", async () => {
    const today = new Date(2026, 4, 30);
    mockReminders.push({
      id: "r7",
      userId: "u1",
      title: "Test",
      reminderDate: new Date(2026, 4, 30),
      isLunar: false,
      recurrence: null,
      notified: false,
      user: { email: "", name: "Tester" },
    });
    const r = await runReminders(today);
    expect(r.fired).toBe(0);
    expect(r.skipped).toBe(1);
  });
});
