import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
import { sanitize, isValidEmail, isRateLimited, hits, RATE_LIMIT } from "./contact.utils.js";

describe("sanitize", () => {
  it("retourne une chaîne vide pour null", () => {
    expect(sanitize(null)).toBe("");
  });

  it("retourne une chaîne vide pour undefined", () => {
    expect(sanitize(undefined)).toBe("");
  });

  it("retourne une chaîne vide pour un objet", () => {
    expect(sanitize({ foo: "bar" })).toBe("");
  });

  it("retourne une chaîne vide pour un tableau", () => {
    expect(sanitize([1, 2, 3])).toBe("");
  });

  it("trim les espaces", () => {
    expect(sanitize("  hello  ")).toBe("hello");
  });

  it("tronque à 2000 caractères", () => {
    expect(sanitize("a".repeat(3000))).toHaveLength(2000);
  });

  it("convertit un nombre", () => {
    expect(sanitize(42)).toBe("42");
  });

  it("convertit un booléen", () => {
    expect(sanitize(true)).toBe("true");
  });

  it("retourne la chaîne intacte", () => {
    expect(sanitize("hello world")).toBe("hello world");
  });
});

describe("isValidEmail", () => {
  it("valide une adresse correcte", () => {
    expect(isValidEmail("user@example.com")).toBe(true);
  });

  it("valide un sous-domaine", () => {
    expect(isValidEmail("user@mail.example.co.uk")).toBe(true);
  });

  it("rejette sans @", () => {
    expect(isValidEmail("userexample.com")).toBe(false);
  });

  it("rejette avec deux @", () => {
    expect(isValidEmail("user@@example.com")).toBe(false);
  });

  it("rejette sans domaine après @", () => {
    expect(isValidEmail("user@")).toBe(false);
  });

  it("rejette sans local avant @", () => {
    expect(isValidEmail("@example.com")).toBe(false);
  });

  it("rejette sans point dans le domaine", () => {
    expect(isValidEmail("user@examplecom")).toBe(false);
  });

  it("rejette le domaine se terminant par un point", () => {
    expect(isValidEmail("user@example.")).toBe(false);
  });

  it("rejette le domaine commençant par un point après @", () => {
    expect(isValidEmail("user@.com")).toBe(false);
  });

  it("rejette une adresse trop longue (> 254)", () => {
    const long = "a".repeat(250) + "@b.com";
    expect(long.length).toBeGreaterThan(254);
    expect(isValidEmail(long)).toBe(false);
  });

  it("rejette un espace dans la partie locale", () => {
    expect(isValidEmail("us er@example.com")).toBe(false);
  });

  it("rejette un espace dans le domaine", () => {
    expect(isValidEmail("user@exam ple.com")).toBe(false);
  });
});

describe("isRateLimited", () => {
  beforeEach(() => {
    hits.clear();
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it("autorise les N premières requêtes", () => {
    for (let i = 0; i < RATE_LIMIT; i++) {
      expect(isRateLimited("1.2.3.4")).toBe(false);
    }
  });

  it("bloque après dépassement du rate limit", () => {
    for (let i = 0; i < RATE_LIMIT; i++) {
      isRateLimited("1.2.3.4");
    }
    expect(isRateLimited("1.2.3.4")).toBe(true);
  });

  it("isole les IPs différentes", () => {
    for (let i = 0; i < RATE_LIMIT; i++) {
      isRateLimited("1.1.1.1");
    }
    expect(isRateLimited("1.1.1.1")).toBe(true);
    expect(isRateLimited("2.2.2.2")).toBe(false);
  });

  it("réinitialise le compteur après la fenêtre de temps", () => {
    for (let i = 0; i <= RATE_LIMIT; i++) {
      isRateLimited("1.2.3.4");
    }
    expect(isRateLimited("1.2.3.4")).toBe(true);

    vi.advanceTimersByTime(15 * 60 * 1001);
    expect(isRateLimited("1.2.3.4")).toBe(false);
  });
});
