/*
 * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
 * Ministerpräsidenten des Landes Schleswig-Holstein
 * Staatskanzlei
 * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
 *
 * Lizenziert unter der EUPL, Version 1.2 oder - sobald
 * diese von der Europäischen Kommission genehmigt wurden -
 * Folgeversionen der EUPL ("Lizenz");
 * Sie dürfen dieses Werk ausschließlich gemäß
 * dieser Lizenz nutzen.
 * Eine Kopie der Lizenz finden Sie hier:
 *
 * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
 *
 * Sofern nicht durch anwendbare Rechtsvorschriften
 * gefordert oder in schriftlicher Form vereinbart, wird
 * die unter der Lizenz verbreitete Software "so wie sie
 * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
 * ausdrücklich oder stillschweigend - verbreitet.
 * Die sprachspezifischen Genehmigungen und Beschränkungen
 * unter der Lizenz sind dem Lizenztext zu entnehmen.
 */
import { formatDate, registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import {
  fixPartialYear,
  formatDateWithoutYearWithTime,
  formatForDatabase,
  formatFullDate,
  formatFullDateWithTimeAndDay,
  formatFullDateWithTimeWithoutSeconds,
  formatFullDateWithoutSeperator,
  formatHourMinute,
  formatToPrettyDate,
  formatWithoutYear,
  isISODateInPast,
  sortByGermanDateStr,
} from './date.util';

import faker from '@faker-js/faker';

import * as dateFns from 'date-fns';

jest.mock('date-fns', () => mockAsEsModule('date-fns'));

registerLocaleData(localeDe);

function mockAsEsModule(module: string) {
  return {
    __esModule: true,
    ...jest.requireActual(module),
  };
}

describe('Date Util', () => {
  const dateToFormat: Date = new Date('1010-01-01:01:01:01');

  describe('formatForDatabase()', () => {
    it('should return correct date string with Date object', () => {
      const formattedDate: string = formatForDatabase(dateToFormat);

      expect(formattedDate).toEqual('1010-01-01');
    });
  });

  it('should return null', () => {
    const formattedDate = formatForDatabase(null);

    expect(formattedDate).toBeNull();
  });

  it('should return full date with hour and minutes', () => {
    const formattedDate: string = formatFullDateWithTimeWithoutSeconds(dateToFormat);

    expect(formattedDate).toEqual('01.01.10 01:01');
  });

  it('should return name of day, full date, hour, minute and seconds', () => {
    const formattedDate: string = formatFullDateWithTimeAndDay(dateToFormat);

    expect(formattedDate).toEqual('Montag, 01.01.1010, 01:01:01');
  });

  it('should return full date', () => {
    const formattedDate: string = formatFullDate(dateToFormat);

    expect(formattedDate).toEqual('01.01.1010');
  });

  it('should return hour and minute', () => {
    const formattedDate: string = formatHourMinute(dateToFormat);

    expect(formattedDate).toEqual('01:01');
  });

  it('should return day and name of month', () => {
    const formattedDate: string = formatWithoutYear(dateToFormat);

    expect(formattedDate).toEqual('01. Jan.');
  });

  it('should return full date without seperator', () => {
    const formattedDate: string = formatFullDateWithoutSeperator(dateToFormat);

    expect(formattedDate).toEqual('10100101');
  });

  it('should return date with month and day and time with hour and minute', () => {
    const formattedDate: string = formatDateWithoutYearWithTime(dateToFormat);

    expect(formattedDate).toEqual('01. Jan. 01:01');
  });

  describe('formatToPrettyDate()', () => {
    it('should format date without year', () => {
      const today: Date = new Date();
      const day: string = formatDate(today, 'dd', 'de');
      const month: string = formatDate(today, 'MMM', 'de');

      const result: string = formatToPrettyDate(today);

      expect(result).toEqual(`${day}. ${month}`);
    });

    it('should format date without year', () => {
      const result: string = formatToPrettyDate(dateToFormat);

      expect(result).toEqual('01.01.1010');
    });
  });

  describe('isISODateInPast', () => {
    it('should call parseISO', () => {
      const parseISO = jest.spyOn(dateFns, 'parseISO');
      const isoDate: string = faker.date.past().toISOString();

      isISODateInPast(isoDate);

      expect(parseISO).toHaveBeenCalledWith(isoDate);
    });

    it('should call isPast', () => {
      const isPast = jest.spyOn(dateFns, 'isPast');
      const date: Date = faker.date.past();
      const isoDate: string = date.toISOString();

      isISODateInPast(isoDate);

      expect(isPast).toHaveBeenCalledWith(date);
    });

    it('should return false for future dates', () => {
      const isoDate: string = faker.date.future().toISOString();

      const result: boolean = isISODateInPast(isoDate);

      expect(result).toBeFalsy();
    });

    it('should return true for past dates', () => {
      const isoDate: string = faker.date.past().toISOString();

      const result: boolean = isISODateInPast(isoDate);

      expect(result).toBeTruthy();
    });
  });

  describe('sort by german date string', () => {
    const veryEarly: TestObject = createTestObject('01.01.2000');
    const early: TestObject = createTestObject('01.01.2020');
    const latest: TestObject = createTestObject('01.01.2100');
    const objects: TestObject[] = [early, latest, veryEarly];

    it('should have latest at first', () => {
      const sorted: TestObject[] = sortByGermanDateStr<TestObject>(
        objects,
        (obj: TestObject) => obj.dateStrField,
      );

      expect(sorted[0]).toEqual(latest);
    });

    it('should have early at second', () => {
      const sorted: TestObject[] = sortByGermanDateStr<TestObject>(
        objects,
        (obj: TestObject) => obj.dateStrField,
      );

      expect(sorted[1]).toEqual(early);
    });

    it('should have very early at last', () => {
      const sorted: TestObject[] = sortByGermanDateStr<TestObject>(
        objects,
        (obj: TestObject) => obj.dateStrField,
      );

      expect(sorted[2]).toEqual(veryEarly);
    });

    function createTestObject(dateStrField: string): TestObject {
      return { dateStrField };
    }

    interface TestObject {
      dateStrField: string;
    }
  });

  describe('fixPartialYear', () => {
    it('should return Date in the 2000 millennium for 2 digit year value', () => {
      const dateInput: Date = new Date('0023-10-22');
      const yearExpected: number = 2023;

      const result: Date = fixPartialYear(dateInput);

      expect(result.getFullYear()).toBe(yearExpected);
    });

    it('should return Date unchanged for non 2 digit year value', () => {
      const dateInput: Date = new Date('2023-10-22');

      const result: Date = fixPartialYear(dateInput);

      expect(result).toBe(dateInput);
    });
  });
});