"use strict";

var _module = require("./module");
let referenceDate = new Date(Date.now());
let birthDate17 = new Date(referenceDate.getFullYear() - 17, referenceDate.getMonth(), referenceDate.getDate());
let birthDate18 = new Date(referenceDate.getFullYear() - 18, referenceDate.getMonth(), referenceDate.getDate());

/**
 * @function calculateAge
 */
describe('calculateAge Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return 17 for birthDate17', () => {
      expect((0, _module.calculateAge)(birthDate17)).toEqual(17);
    });
    it('should return 18 for birthDate18', () => {
      expect((0, _module.calculateAge)(birthDate18)).toEqual(18);
    });
  });
  describe('invalid data tests', () => {
    it('should not return 18 for birthDate17', () => {
      expect((0, _module.calculateAge)(birthDate17)).not.toEqual(18);
    });
    it('should throw a "missing param birthDate" error', () => {
      expect(() => (0, _module.calculateAge)()).toThrow("missing param birthDate");
    });
    it('should throw a "birthDate is not a Date" error', () => {
      expect(() => (0, _module.calculateAge)("not a Date")).toThrow("birthDate is not a Date");
    });
    it('should throw a "birthDate is not a valid Date" error', () => {
      expect(() => (0, _module.calculateAge)(new Date("not a valid Date"))).toThrow("birthDate is not a valid Date");
    });
  });
});

/**
 * @function validateName
 */
describe('validateName Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return "John" for "John"', () => {
      expect((0, _module.validateName)("John")).toEqual("John");
    });
    it('should return "François" for accented name', () => {
      expect((0, _module.validateName)("François")).toEqual("François");
    });
    it('should return "Jean-Pierre" for hyphenated name', () => {
      expect((0, _module.validateName)("Jean-Pierre")).toEqual("Jean-Pierre");
    });
    it("should return \"O'Brien\" for apostrophe name", () => {
      expect((0, _module.validateName)("O'Brien")).toEqual("O'Brien");
    });
    it('should return "  Marie  " trimmed', () => {
      expect((0, _module.validateName)("  Marie  ")).toEqual("Marie");
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "name is required" error', () => {
      expect(() => (0, _module.validateName)()).toThrow("name is required");
    });
    it('should throw a "name must be a string" error', () => {
      expect(() => (0, _module.validateName)(123)).toThrow("name must be a string");
    });
    it('should throw a "name must be a valid name" error', () => {
      expect(() => (0, _module.validateName)("John-123")).toThrow("name must be a valid name");
    });
  });
});

/**
 * @function validatePrenom
 */
describe('validatePrenom Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return "Doe" for "Doe"', () => {
      expect((0, _module.validatePrenom)("Doe")).toEqual("Doe");
    });
    it('should return "Zoë" for prenom with diaeresis', () => {
      expect((0, _module.validatePrenom)("Zoë")).toEqual("Zoë");
    });
    it('should return "Anne Marie" for compound prenom', () => {
      expect((0, _module.validatePrenom)("Anne Marie")).toEqual("Anne Marie");
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "prenom is required" error', () => {
      expect(() => (0, _module.validatePrenom)()).toThrow("prenom is required");
    });
    it('should throw a "prenom must be a string" error', () => {
      expect(() => (0, _module.validatePrenom)(123)).toThrow("prenom must be a string");
    });
    it('should throw a "prenom must be a valid prenom" error', () => {
      expect(() => (0, _module.validatePrenom)("Doe-123")).toThrow("prenom must be a valid prenom");
    });
  });
});

/**
 * @function validateEmail
 */
describe('validateEmail Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return "john.doe@example.com" for "john.doe@example.com"', () => {
      expect((0, _module.validateEmail)("john.doe@example.com")).toEqual("john.doe@example.com");
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "email is required" error', () => {
      expect(() => (0, _module.validateEmail)()).toThrow("email is required");
    });
    it('should throw a "email must be a string" error', () => {
      expect(() => (0, _module.validateEmail)(123)).toThrow("email must be a string");
    });
    it('should throw a "email must be a valid email address" error', () => {
      expect(() => (0, _module.validateEmail)("john.doe@example")).toThrow("email must be a valid email address");
    });
  });
});

/**
 * @function validateDateOfBirth
 */
describe('validateDateOfBirth Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return "1990/01/01" for "1990/01/01"', () => {
      expect((0, _module.validateDateOfBirth)("1990/01/01")).toEqual("1990/01/01");
    });
    it('should return "1990-01-01" for "1990-01-01"', () => {
      expect((0, _module.validateDateOfBirth)("1990-01-01")).toEqual("1990-01-01");
    });
    it('should return personnalized date of birth for birthDate18', () => {
      expect((0, _module.validateDateOfBirth)(birthDate18.toISOString().split('T')[0])).toEqual(birthDate18.toISOString().split('T')[0]);
    });
    it('should return "01/01/1990" for "01/01/1990"', () => {
      expect((0, _module.validateDateOfBirth)("01/01/1990")).toEqual("01/01/1990");
    });
    it('should return "01-01-1990" for "01-01-1990"', () => {
      expect((0, _module.validateDateOfBirth)("01-01-1990")).toEqual("01-01-1990");
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "date of birth is required" error', () => {
      expect(() => (0, _module.validateDateOfBirth)()).toThrow("date of birth is required");
    });
    it('should throw a "date of birth must be a string" error', () => {
      expect(() => (0, _module.validateDateOfBirth)(123)).toThrow("date of birth must be a string");
    });
    it('should throw a "date of birth must be a valid date" error', () => {
      expect(() => (0, _module.validateDateOfBirth)("not a valid date")).toThrow("date of birth must be a valid date");
    });
    it('should throw a "date of birth must be at least 18 years old" error', () => {
      expect(() => (0, _module.validateDateOfBirth)(birthDate17.toISOString().split('T')[0])).toThrow("date of birth must be at least 18 years old");
    });
    it('should throw for impossible calendar date "31/02/1990"', () => {
      expect(() => (0, _module.validateDateOfBirth)("31/02/1990")).toThrow("date of birth must be a valid date");
    });
  });
});

/**
 * @function validateVille
 */
describe('validateVille Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return "Paris" for "Paris"', () => {
      expect((0, _module.validateVille)("Paris")).toEqual("Paris");
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "ville is required" error', () => {
      expect(() => (0, _module.validateVille)()).toThrow("ville is required");
    });
    it('should throw a "ville must be a string" error', () => {
      expect(() => (0, _module.validateVille)(123)).toThrow("ville must be a string");
    });
    it('should throw a "ville must be a valid ville" error', () => {
      expect(() => (0, _module.validateVille)("Paris-123")).toThrow("ville must be a valid ville");
    });
  });
});

/**
 * @function validateCodePostal
 */
describe('validateCodePostal Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return "75001" for "75001"', () => {
      expect((0, _module.validateCodePostal)("75001")).toEqual("75001");
    });
    it('should return "20200" for "20200" (Corse)', () => {
      expect((0, _module.validateCodePostal)("20200")).toEqual("20200");
    });
    it('should return "97400" for "97400" (DOM)', () => {
      expect((0, _module.validateCodePostal)("97400")).toEqual("97400");
    });
    it('should return "98800" for "98800" (TOM)', () => {
      expect((0, _module.validateCodePostal)("98800")).toEqual("98800");
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "code postal is required" error', () => {
      expect(() => (0, _module.validateCodePostal)()).toThrow("code postal is required");
    });
    it('should throw a "code postal must be a string" error', () => {
      expect(() => (0, _module.validateCodePostal)(123)).toThrow("code postal must be a string");
    });
    it('should throw a "code postal must be a valid code postal" error', () => {
      expect(() => (0, _module.validateCodePostal)("123456")).toThrow("code postal must be a valid code postal");
    });
    it('should throw a "code postal must be a valid code postal" error for 00123', () => {
      expect(() => (0, _module.validateCodePostal)("00123")).toThrow("code postal must be a valid code postal");
    });
    it('should throw a "code postal must be a valid code postal" error for 96000', () => {
      expect(() => (0, _module.validateCodePostal)("96000")).toThrow("code postal must be a valid code postal");
    });
    it('should throw a "code postal must be a valid code postal" error for 97900', () => {
      expect(() => (0, _module.validateCodePostal)("97900")).toThrow("code postal must be a valid code postal");
    });
    it('should throw a "code postal must be a valid code postal" error for 2A000', () => {
      expect(() => (0, _module.validateCodePostal)("2A000")).toThrow("code postal must be a valid code postal");
    });
  });
});

/**
 * @function validateFormData
 */
describe('validateFormData Unit Test Suites', () => {
  describe('valid data tests', () => {
    it('should return validated plain object data', () => {
      const formData = new FormData();
      formData.append('nom', "John");
      formData.append('prenom', "Doe");
      formData.append('email', "john.doe@example.com");
      formData.append('dateOfBirth', "1990/01/01");
      formData.append('ville', "Paris");
      formData.append('codePostal', "75001");
      expect((0, _module.validateFormData)(formData)).toEqual({
        nom: "John",
        prenom: "Doe",
        email: "john.doe@example.com",
        dateOfBirth: "1990/01/01",
        ville: "Paris",
        codePostal: "75001"
      });
    });
    it('should return validated plain object data for birthDate18', () => {
      const formData = new FormData();
      formData.append('nom', "John");
      formData.append('prenom', "Doe");
      formData.append('email', "john.doe@example.com");
      formData.append('dateOfBirth', birthDate18.toISOString().split('T')[0]);
      formData.append('ville', "Paris");
      formData.append('codePostal', "75001");
      expect((0, _module.validateFormData)(formData)).toEqual({
        nom: "John",
        prenom: "Doe",
        email: "john.doe@example.com",
        dateOfBirth: birthDate18.toISOString().split('T')[0],
        ville: "Paris",
        codePostal: "75001"
      });
    });
  });
  describe('invalid data tests', () => {
    it('should throw a "form data is required" error', () => {
      expect(() => (0, _module.validateFormData)()).toThrow("form data is required");
    });
    it('should throw a "form data must be a FormData" error', () => {
      expect(() => (0, _module.validateFormData)(123)).toThrow("form data must be a FormData");
    });
  });
});

/**
 * @function isAdult
 */
describe('isAdult Unit Test Suites', () => {
  it('should return false for a 17 years old person', () => {
    expect((0, _module.isAdult)(birthDate17)).toBe(false);
  });
  it('should return true for an 18 years old person', () => {
    expect((0, _module.isAdult)(birthDate18)).toBe(true);
  });
  it('should return true for a person older than 18', () => {
    const birthDate30 = new Date(referenceDate.getFullYear() - 30, referenceDate.getMonth(), referenceDate.getDate());
    expect((0, _module.isAdult)(birthDate30)).toBe(true);
  });
});

/**
 * @function isFrenchCodePostal
 */
describe('isFrenchCodePostal Unit Test Suites', () => {
  it('should return true for metropolitan postal code', () => {
    expect((0, _module.isFrenchCodePostal)("75001")).toBe(true);
  });
  it('should return true for overseas postal code', () => {
    expect((0, _module.isFrenchCodePostal)("98800")).toBe(true);
  });
  it('should return false when value is not a string', () => {
    expect((0, _module.isFrenchCodePostal)(75001)).toBe(false);
  });
});

/**
 * @function localStorage helpers
 */
describe('localStorage helper Unit Test Suites', () => {
  beforeEach(() => {
    localStorage.clear();
  });
  it('should save and retrieve form data', () => {
    const payload = {
      nom: "John",
      codePostal: "75001"
    };
    (0, _module.saveFormData)(payload);
    expect((0, _module.getFormData)()).toEqual(payload);
  });
  it('should clear saved form data', () => {
    (0, _module.saveFormData)({
      nom: "John"
    });
    (0, _module.clearFormData)();
    expect(localStorage.getItem('formData')).toBeNull();
  });
  it('should return null when no saved form data exists', () => {
    expect((0, _module.getFormData)()).toBeNull();
  });
  it('should throw when saved form data is not valid JSON', () => {
    localStorage.setItem('formData', "{invalid json");
    expect(() => (0, _module.getFormData)()).toThrow("unable to parse saved form data");
  });
  it('should throw when localStorage setItem fails', () => {
    const setItemSpy = jest.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
      throw new Error("quota exceeded");
    });
    expect(() => (0, _module.saveFormData)({
      nom: "John"
    })).toThrow("unable to save form data");
    setItemSpy.mockRestore();
  });
});

/**
 * @function registrations localStorage helpers
 */
describe('registrations localStorage helper Unit Test Suites', () => {
  beforeEach(() => {
    localStorage.clear();
  });
  it('should save and retrieve registrations list', () => {
    const registrations = [{
      nom: "John"
    }, {
      nom: "Alice"
    }];
    (0, _module.saveRegistrations)(registrations);
    expect((0, _module.getRegistrations)()).toEqual(registrations);
  });
  it('should return empty array when no registration exists', () => {
    expect((0, _module.getRegistrations)()).toEqual([]);
  });
  it('should throw when registrations are not a JSON array', () => {
    localStorage.setItem('registrations', JSON.stringify({
      nom: "John"
    }));
    expect(() => (0, _module.getRegistrations)()).toThrow("unable to parse saved registrations");
  });
  it('should throw when saved registrations are invalid JSON', () => {
    localStorage.setItem('registrations', '{invalid json');
    expect(() => (0, _module.getRegistrations)()).toThrow("unable to parse saved registrations");
  });
  it('should append a registration to existing registrations', () => {
    (0, _module.saveRegistrations)([{
      nom: "John"
    }]);
    (0, _module.appendRegistration)({
      nom: "Alice"
    });
    expect((0, _module.getRegistrations)()).toEqual([{
      nom: "John"
    }, {
      nom: "Alice"
    }]);
  });
  it('should throw when localStorage setItem fails for registrations', () => {
    const setItemSpy = jest.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
      throw new Error("quota exceeded");
    });
    expect(() => (0, _module.saveRegistrations)([{
      nom: "John"
    }])).toThrow("unable to save registrations");
    setItemSpy.mockRestore();
  });
});

/**
 * @function handleSubmit
 */
describe('handleSubmit Unit Test Suites', () => {
  beforeEach(() => {
    localStorage.clear();
  });
  it('should prevent default and append validated data in localStorage registrations', () => {
    const form = document.createElement('form');
    form.innerHTML = "\n      <input name=\"nom\" value=\"John\" />\n      <input name=\"prenom\" value=\"Doe\" />\n      <input name=\"email\" value=\"john.doe@example.com\" />\n      <input name=\"dateOfBirth\" value=\"1990/01/01\" />\n      <input name=\"ville\" value=\"Paris\" />\n      <input name=\"codePostal\" value=\"75001\" />\n    ";
    const fakeEvent = {
      target: form,
      preventDefault: jest.fn()
    };
    const submittedRegistration = (0, _module.handleSubmit)(fakeEvent);
    expect(fakeEvent.preventDefault).toHaveBeenCalledTimes(1);
    expect(submittedRegistration).toEqual({
      nom: "John",
      prenom: "Doe",
      email: "john.doe@example.com",
      dateOfBirth: "1990/01/01",
      ville: "Paris",
      codePostal: "75001"
    });
    expect((0, _module.getRegistrations)()).toEqual([submittedRegistration]);
  });
});