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

// Mock db module
vi.mock("./db", () => ({
  getEmailActionToken: vi.fn(),
  getRequestById: vi.fn(),
  updateRequest: vi.fn(),
  createRequestHistory: vi.fn(),
  markTokenAsUsed: vi.fn(),
  createEmailActionToken: vi.fn(),
}));

import * as db from "./db";

describe("Email Action Token Logic", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("should reject an invalid token", async () => {
    (db.getEmailActionToken as any).mockResolvedValue(null);
    
    const token = await db.getEmailActionToken("invalid-token");
    expect(token).toBeNull();
  });

  it("should reject an already used token", async () => {
    (db.getEmailActionToken as any).mockResolvedValue({
      id: 1,
      token: "test-token",
      requestId: 1,
      action: "validee",
      used: true,
      expiresAt: new Date(Date.now() + 86400000),
      usedAt: new Date(),
      usedBy: 1,
      createdAt: new Date(),
    });

    const token = await db.getEmailActionToken("test-token");
    expect(token).not.toBeNull();
    expect(token!.used).toBe(true);
  });

  it("should reject an expired token", async () => {
    const expiredDate = new Date(Date.now() - 86400000); // 1 day ago
    (db.getEmailActionToken as any).mockResolvedValue({
      id: 1,
      token: "expired-token",
      requestId: 1,
      action: "validee",
      used: false,
      expiresAt: expiredDate,
      usedAt: null,
      usedBy: null,
      createdAt: new Date(),
    });

    const token = await db.getEmailActionToken("expired-token");
    expect(token).not.toBeNull();
    expect(new Date() > token!.expiresAt).toBe(true);
  });

  it("should accept a valid token for validation", async () => {
    const futureDate = new Date(Date.now() + 7 * 86400000); // 7 days from now
    (db.getEmailActionToken as any).mockResolvedValue({
      id: 1,
      token: "valid-token",
      requestId: 5,
      action: "validee",
      used: false,
      expiresAt: futureDate,
      usedAt: null,
      usedBy: null,
      createdAt: new Date(),
    });

    (db.getRequestById as any).mockResolvedValue({
      id: 5,
      titre: "Subvention test",
      type: "subvention_projet",
      status: "soumise",
      description: "Test description",
      montantDemande: 100000,
      associationId: 1,
    });

    const token = await db.getEmailActionToken("valid-token");
    expect(token).not.toBeNull();
    expect(token!.used).toBe(false);
    expect(new Date() < token!.expiresAt).toBe(true);
    expect(token!.action).toBe("validee");

    const request = await db.getRequestById(token!.requestId);
    expect(request).not.toBeNull();
    expect(request!.status).not.toBe("validee");
    expect(request!.status).not.toBe("refusee");
  });

  it("should accept a valid token for refusal", async () => {
    const futureDate = new Date(Date.now() + 7 * 86400000);
    (db.getEmailActionToken as any).mockResolvedValue({
      id: 2,
      token: "refuse-token",
      requestId: 5,
      action: "refusee",
      used: false,
      expiresAt: futureDate,
      usedAt: null,
      usedBy: null,
      createdAt: new Date(),
    });

    const token = await db.getEmailActionToken("refuse-token");
    expect(token).not.toBeNull();
    expect(token!.action).toBe("refusee");
  });

  it("should reject processing an already validated request", async () => {
    (db.getRequestById as any).mockResolvedValue({
      id: 5,
      titre: "Subvention test",
      type: "subvention_projet",
      status: "validee",
      associationId: 1,
    });

    const request = await db.getRequestById(5);
    expect(request!.status === "validee" || request!.status === "refusee").toBe(true);
  });

  it("should create email action tokens with correct expiry", async () => {
    (db.createEmailActionToken as any).mockResolvedValue("new-token");

    const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
    
    await db.createEmailActionToken({
      token: "validate-token-123",
      requestId: 10,
      action: "validee",
      expiresAt,
    });

    expect(db.createEmailActionToken).toHaveBeenCalledWith({
      token: "validate-token-123",
      requestId: 10,
      action: "validee",
      expiresAt,
    });
  });

  it("should mark token as used after processing", async () => {
    (db.markTokenAsUsed as any).mockResolvedValue(undefined);

    await db.markTokenAsUsed("used-token", 1);

    expect(db.markTokenAsUsed).toHaveBeenCalledWith("used-token", 1);
  });
});
