import { Configuration } from '../../configuration/configuration.entity';
import { WorkingHours } from './working-hours';

describe('WorkingHours', () => {
  it('should create an instance', () => {
    const parsed = [
      null,
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      null,
    ];

    const wh = new WorkingHours('-;08:00-17:00;+;+;+;+;-');

    expect(wh).toBeDefined();
    expect(wh.workingHours).toEqual(parsed);
  });

  it('should create an instance from Configuration', async () => {
    const testStr = '-;08:00-17:00;+;+;+;+;-';

    const parsed = [
      null,
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      [['08:00', '17:00']],
      null,
    ];

    Configuration.get = jest.fn().mockResolvedValue(testStr);

    const wh = await WorkingHours.createFromConfiguration();

    expect(Configuration.get).toBeCalledTimes(1);
    expect(wh).toBeDefined();
    expect(wh.workingHours).toEqual(parsed);
  });

  it('should parse with modifiers', () => {
    const testStr = '-;08:00-13:00,14:00-17:00;+;-;+;08:00-17:00;-';

    const parsed = [
      null,
      [
        ['08:00', '13:00'],
        ['14:00', '17:00'],
      ],
      [
        ['08:00', '13:00'],
        ['14:00', '17:00'],
      ],
      null,
      [
        ['08:00', '13:00'],
        ['14:00', '17:00'],
      ],
      [['08:00', '17:00']],
      null,
    ];

    const wh = new WorkingHours(testStr);

    expect(wh).toBeDefined();
    expect(wh.workingHours).toEqual(parsed);
  });

  it('should fail to parse if not 7 days of the week are provided', () => {
    expect(() => new WorkingHours('08:00-17:00;+;+;+;+;-')).toThrow();
    expect(() => new WorkingHours('08:00-17:00;+;+;+;+;-;-;-')).toThrow();
  });

  it('should fail if no ranges provided', () => {
    expect(() => new WorkingHours('-;+;+;+;+;+;-')).toThrow();
  });

  it('should fail if wrong format ranges provided', () => {
    expect(() => new WorkingHours('08:00-13:00-15:00;-;-;-;-;-;-')).toThrow();
    expect(() => new WorkingHours('0800-13:00;-;-;-;-;-;-')).toThrow();
    expect(() => new WorkingHours('08:00;-;-;-;-;-;-')).toThrow();
    expect(
      () => new WorkingHours('08:00-13:00,1400-17:00;-;-;-;-;-;-')
    ).toThrow();
  });

  it('should return correct active state', () => {
    const wh = new WorkingHours(
      '-;08:00-13:00,14:00-17:00;+;+;+;08:00-15:00;-'
    );

    // Sunday
    expect(wh.isDateActive(new Date('2021-07-11T13:00:00.000Z'))).toBe(false);

    // Define unified function to test all the other days since they should have the same ranges
    function testMondayRange(dayIncrement: number) {
      const day = 12 + dayIncrement;
      expect(wh.isDateActive(new Date(`2021-07-${day}T07:00:00.000Z`))).toBe(
        false
      );
      expect(wh.isDateActive(new Date(`2021-07-${day}T09:00:00.000Z`))).toBe(
        true
      );
      expect(wh.isDateActive(new Date(`2021-07-${day}T13:30:00.000Z`))).toBe(
        false
      );
      expect(wh.isDateActive(new Date(`2021-07-${day}T15:00:00.000Z`))).toBe(
        true
      );
      expect(wh.isDateActive(new Date(`2021-07-${day}T18:00:00.000Z`))).toBe(
        false
      );
    }
    // Monday
    testMondayRange(0);

    // Tuesday
    testMondayRange(1);

    // Wednesday
    testMondayRange(2);

    // Thursday
    testMondayRange(3);

    // Friday
    expect(wh.isDateActive(new Date('2021-07-16T07:00:00.000Z'))).toBe(false);
    expect(wh.isDateActive(new Date('2021-07-16T12:00:00.000Z'))).toBe(true);
    expect(wh.isDateActive(new Date('2021-07-16T17:00:00.000Z'))).toBe(false);

    // Saturday
    expect(wh.isDateActive(new Date('2021-07-17T13:00:00.000Z'))).toBe(false);
  });

  it('should return correct next active date', () => {
    const wh = new WorkingHours(
      '-;08:00-13:00,14:00-17:00;+;+;+;08:00-15:00;-'
    );

    // Sunday
    expect(
      wh.getNextActiveDate(new Date('2021-07-11T13:00:00.000Z')).toISOString()
    ).toBe('2021-07-12T08:00:00.000Z');

    // Define unified function to test all the other days since they should have the same ranges
    function testMonday(dayIncrement: number, includeLast = true) {
      const day = 12 + dayIncrement;
      expect(
        wh
          .getNextActiveDate(new Date(`2021-07-${day}T07:00:00.000Z`))
          .toISOString()
      ).toBe(`2021-07-${day}T08:00:00.000Z`);
      expect(
        wh
          .getNextActiveDate(new Date(`2021-07-${day}T13:30:00.000Z`))
          .toISOString()
      ).toBe(`2021-07-${day}T14:00:00.000Z`);
      if (includeLast) {
        expect(
          wh
            .getNextActiveDate(new Date(`2021-07-${day}T18:00:00.000Z`))
            .toISOString()
        ).toBe(`2021-07-${day + 1}T08:00:00.000Z`);
      }
    }

    // Monday
    testMonday(0);

    // Tuesday
    testMonday(1);

    // Wednesday
    testMonday(2);

    // Thursday
    testMonday(3, false);
    expect(
      wh.getNextActiveDate(new Date(`2021-07-15T18:00:00.000Z`)).toISOString()
    ).toBe(`2021-07-16T08:00:00.000Z`);

    // Friday
    expect(
      wh.getNextActiveDate(new Date(`2021-07-16T07:00:00.000Z`)).toISOString()
    ).toBe(`2021-07-16T08:00:00.000Z`);
    expect(
      wh.getNextActiveDate(new Date(`2021-07-16T16:00:00.000Z`)).toISOString()
    ).toBe(`2021-07-19T08:00:00.000Z`);

    // Saturday
    expect(
      wh.getNextActiveDate(new Date('2021-07-17T13:00:00.000Z')).toISOString()
    ).toBe('2021-07-19T08:00:00.000Z');
  });
});
