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

vi.mock("./db", () => ({
  getMaterialReturnFollowupByRequestId: vi.fn(),
  upsertMaterialReturnFollowup: vi.fn(),
}));

vi.mock("./mailer", () => ({
  sendOperationalEmail: vi.fn(),
}));

vi.mock("./materialReturnEmail", () => ({
  generateMaterialReturnReminderEmail: vi.fn(),
}));

vi.mock("./materialReturnPdf", () => ({
  generateMaterialReturnStatementPdf: vi.fn(),
}));

vi.mock("./logisticsSettings", () => ({
  getLogisticsSettings: vi.fn(),
}));

import * as db from "./db";
import { getLogisticsSettings } from "./logisticsSettings";
import { scheduleMaterialReturnFollowup } from "./materialReturnWorkflow";

describe("scheduleMaterialReturnFollowup", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(getLogisticsSettings).mockResolvedValue({
      materialReturnLeadDays: 4,
      materialReturnGraceDays: 3,
      inventory: {
        tente3x3: 12,
        chapiteau5x5: 10,
        podium: 1,
        autres: null,
      },
      replacementValues: {
        tente3x3: 0,
        chapiteau5x5: 0,
        podium: 0,
        autres: null,
      },
    });
  });

  it("keeps the standard expiry window for a fresh follow-up", async () => {
    vi.mocked(db.getMaterialReturnFollowupByRequestId).mockResolvedValue(undefined);
    vi.mocked(db.upsertMaterialReturnFollowup).mockResolvedValue(1);

    const request = {
      id: 12,
      associationId: 3,
      formData: JSON.stringify({ dateRestitution: "2026-06-15" }),
    } as any;

    await scheduleMaterialReturnFollowup({
      request,
      serviceLabel: "Service technique",
      recipientEmails: ["agent@example.com"],
    });

    expect(db.upsertMaterialReturnFollowup).toHaveBeenCalledTimes(1);
    const payload = vi.mocked(db.upsertMaterialReturnFollowup).mock.calls[0]?.[1];
    expect(payload.status).toBe("planifie");
    expect(new Date(payload.uploadTokenExpiresAt).toISOString().startsWith("2026-06-18")).toBe(true);
  });

  it("reactivates an expired upload window when the follow-up is reassigned", async () => {
    const expiredDate = new Date();
    expiredDate.setDate(expiredDate.getDate() - 2);
    expiredDate.setHours(23, 59, 59, 999);

    vi.mocked(db.getMaterialReturnFollowupByRequestId).mockResolvedValue({
      id: 4,
      requestId: 12,
      associationId: 3,
      status: "en_cours",
      uploadToken: "existing-token",
      uploadTokenExpiresAt: expiredDate,
    } as any);
    vi.mocked(db.upsertMaterialReturnFollowup).mockResolvedValue(4);

    const request = {
      id: 12,
      associationId: 3,
      formData: JSON.stringify({ dateRestitution: "2026-05-01" }),
    } as any;

    await scheduleMaterialReturnFollowup({
      request,
      serviceLabel: "Service technique",
      recipientEmails: ["agent@example.com"],
    });

    const payload = vi.mocked(db.upsertMaterialReturnFollowup).mock.calls[0]?.[1];
    expect(payload.uploadToken).toBe("existing-token");
    expect(new Date(payload.uploadTokenExpiresAt).getTime()).toBeGreaterThan(Date.now());
    expect(payload.status).toBe("planifie");
  });

  it("generates a new token and a fresh upload window when reactivation is explicit", async () => {
    const futureExpiry = new Date("2026-06-18T23:59:59.999Z");

    vi.mocked(db.getMaterialReturnFollowupByRequestId).mockResolvedValue({
      id: 4,
      requestId: 12,
      associationId: 3,
      status: "en_attente",
      uploadToken: "existing-token",
      uploadTokenExpiresAt: futureExpiry,
    } as any);
    vi.mocked(db.upsertMaterialReturnFollowup).mockResolvedValue(4);

    const request = {
      id: 12,
      associationId: 3,
      formData: JSON.stringify({ dateRestitution: "2026-06-15" }),
    } as any;
    const reactivatedAt = new Date("2026-06-20T10:30:00.000Z");

    await scheduleMaterialReturnFollowup({
      request,
      serviceLabel: "Service technique",
      recipientEmails: ["agent@example.com"],
    }, {
      forceNewToken: true,
      forceImmediateSendWindow: true,
      reactivatedByUserId: 9,
      reactivatedAt,
      reactivationReason: "Suivi rouvert",
    });

    const payload = vi.mocked(db.upsertMaterialReturnFollowup).mock.calls[0]?.[1];
    expect(payload.uploadToken).not.toBe("existing-token");
    expect(new Date(payload.uploadTokenExpiresAt).getTime()).toBeGreaterThan(futureExpiry.getTime());
    expect(new Date(payload.uploadTokenExpiresAt).getTime()).toBeGreaterThan(reactivatedAt.getTime());
    expect(payload.reactivatedByUserId).toBe(9);
    expect(payload.reactivationReason).toBe("Suivi rouvert");
  });
});
