import { TestBed, ComponentFixture } from '@angular/core/testing';
import { CalendarComponent } from './calendar.component';
import { CalendarModule } from './calendar.module';

describe('CalendarComponent', () => {
  let fixture: ComponentFixture<CalendarComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [CalendarModule],
    }).compileComponents();

    fixture = TestBed.createComponent(CalendarComponent);
  });

  it('should create the component', () => {
    expect(fixture.componentInstance).toBeTruthy();
  });

  describe('#changeMonth', () => {
    it('should change the month to 1 (January) 2020', () => {
      spyOn(fixture.componentInstance.monthChange, 'emit').and.callThrough();
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 12;
      fixture.componentInstance.mode = 'month';

      fixture.componentInstance.changeMonth(1);

      expect(fixture.componentInstance.year).toEqual(2020);
      expect(fixture.componentInstance.month).toEqual(1);

      expect(fixture.componentInstance.monthChange.emit).toHaveBeenCalledWith(1);
      expect(fixture.componentInstance.yearChange.emit).toHaveBeenCalledWith(2020);
    });

    it('should change the month to 12 (December) 2018', () => {
      spyOn(fixture.componentInstance.monthChange, 'emit').and.callThrough();
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 1;
      fixture.componentInstance.mode = 'month';

      fixture.componentInstance.changeMonth(-1);

      expect(fixture.componentInstance.month).toEqual(12);
      expect(fixture.componentInstance.year).toEqual(2018);

      expect(fixture.componentInstance.monthChange.emit).toHaveBeenCalledWith(12);
      expect(fixture.componentInstance.yearChange.emit).toHaveBeenCalledWith(2018);
    });

    it('should change the month to 2 (February)', () => {
      spyOn(fixture.componentInstance.monthChange, 'emit').and.callThrough();
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2020;
      fixture.componentInstance.month = 1;
      fixture.componentInstance.mode = 'month';

      fixture.componentInstance.changeMonth(1);

      expect(fixture.componentInstance.year).toEqual(2020);
      expect(fixture.componentInstance.month).toEqual(2);

      expect(fixture.componentInstance.monthChange.emit).toHaveBeenCalledWith(2);
      expect(fixture.componentInstance.yearChange.emit).not.toHaveBeenCalled();
    });

    it('should change the month to 5 (May)', () => {
      spyOn(fixture.componentInstance.monthChange, 'emit').and.callThrough();
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.mode = 'month';

      fixture.componentInstance.changeMonth(-1);

      expect(fixture.componentInstance.month).toEqual(5);
      expect(fixture.componentInstance.year).toEqual(2019);

      expect(fixture.componentInstance.monthChange.emit).toHaveBeenCalledWith(5);
      expect(fixture.componentInstance.yearChange.emit).not.toHaveBeenCalled();
    });

    it('should not change the month if next month is past maxDate', () => {
      spyOn(fixture.componentInstance.monthChange, 'emit').and.callThrough();
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.maxDate = new Date(2019, 5, 1);
      fixture.componentInstance.mode = 'month';

      fixture.componentInstance.changeMonth(1);

      expect(fixture.componentInstance.month).toEqual(6);
      expect(fixture.componentInstance.year).toEqual(2019);

      expect(fixture.componentInstance.monthChange.emit).not.toHaveBeenCalled();
      expect(fixture.componentInstance.yearChange.emit).not.toHaveBeenCalled();
    });

    it('should not change the month if next month is past minDate', () => {
      spyOn(fixture.componentInstance.monthChange, 'emit').and.callThrough();
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.minDate = new Date(2019, 6, 1);
      fixture.componentInstance.mode = 'month';

      fixture.componentInstance.changeMonth(-1);

      expect(fixture.componentInstance.month).toEqual(6);
      expect(fixture.componentInstance.year).toEqual(2019);

      expect(fixture.componentInstance.monthChange.emit).not.toHaveBeenCalled();
      expect(fixture.componentInstance.yearChange.emit).not.toHaveBeenCalled();
    });
  });

  describe('#changeWeek', () => {
    it('should change the week to 52 (December) 2019', () => {
      fixture.componentInstance.year = 2020;
      fixture.componentInstance.week = 1;
      fixture.componentInstance.mode = 'week';

      fixture.componentInstance.changeWeek(-1);

      expect(fixture.componentInstance.year).toEqual(2019);
      expect(fixture.componentInstance.month).toEqual(12);
      expect(fixture.componentInstance.week).toEqual(52);
    });

    it('should change the week to 1 (January) 2020', () => {
      fixture.componentInstance.year = 2019;
      fixture.componentInstance.week = 52;
      fixture.componentInstance.mode = 'week';

      fixture.componentInstance.changeWeek(1);

      expect(fixture.componentInstance.year).toEqual(2020);
      expect(fixture.componentInstance.month).toEqual(1);
      expect(fixture.componentInstance.week).toEqual(1);
    });
  });

  describe('#changeYear', () => {
    it('should change the year from 2018 to 2019', () => {
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2018;
      fixture.componentInstance.changeYear(+1);
      expect(fixture.componentInstance.year).toEqual(2019);

      expect(fixture.componentInstance.yearChange.emit).toHaveBeenCalledWith(2019);
    });

    it('should change the year from 2018 to 2017', () => {
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2018;
      fixture.componentInstance.changeYear(-1);
      expect(fixture.componentInstance.year).toEqual(2017);

      expect(fixture.componentInstance.yearChange.emit).toHaveBeenCalledWith(2017);
    });

    it('should not change the year if next year is past maxDate', () => {
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2018;
      fixture.componentInstance.maxDate = new Date(2018, 0, 1);
      fixture.componentInstance.changeYear(1);
      expect(fixture.componentInstance.year).toEqual(2018);

      expect(fixture.componentInstance.yearChange.emit).not.toHaveBeenCalled();
    });

    it('should not change the year if next year is past minDate', () => {
      spyOn(fixture.componentInstance.yearChange, 'emit').and.callThrough();

      fixture.componentInstance.year = 2018;
      fixture.componentInstance.minDate = new Date(2018, 0, 1);
      fixture.componentInstance.changeYear(-1);
      expect(fixture.componentInstance.year).toEqual(2018);

      expect(fixture.componentInstance.yearChange.emit).not.toHaveBeenCalled();
    });
  });

  describe('calendar header', () => {
    it('should render the header title Juni 2019', () => {
      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;

      fixture.detectChanges();

      const componentElement: Element = fixture.nativeElement;

      expect(componentElement.querySelector('.header .title').textContent).toContain('Juni 2019');
    });
  });

  describe('calendar item indicator', () => {
    it('should render the indicator for month', () => {
      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.items = [
        {
          color: 'red',
          date: new Date(2019, 5, 4),
          title: 'test 1',
          id: 1,
        },
        {
          color: 'red',
          date: new Date(2019, 5, 23),
          title: 'test 4',
          id: 2,
        },
        {
          color: 'white',
          date: new Date(2019, 5, 23),
          title: 'test 5',
          id: 3,
        },
      ];

      fixture.detectChanges();

      const componentElement: Element = fixture.nativeElement;

      const item1: HTMLElement = componentElement.querySelector('.day-cell[day="4"] .indicator[title="test 1"]');

      expect(item1).not.toBeNull();
      expect(item1.style.backgroundColor).toBe('red');

      const item2: HTMLElement = componentElement.querySelector('.day-cell[day="23"] .indicator[title="test 4"]');

      expect(item2).not.toBeNull();
      expect(item2.style.backgroundColor).toBe('red');

      const item3: HTMLElement = componentElement.querySelector('.day-cell[day="23"] .indicator[title="test 5"]');

      expect(item3).not.toBeNull();
      expect(item3.style.backgroundColor).toBe('white');
    });

    it('should render the indicator for week', () => {
      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.week = 23;
      fixture.componentInstance.mode = 'week';
      fixture.componentInstance.items = [
        {
          color: 'red',
          date: new Date(2019, 5, 4),
          title: 'test 1',
          id: 1,
        },
        {
          color: 'red',
          date: new Date(2019, 5, 6),
          title: 'test 4',
          id: 2,
        },
        {
          color: 'white',
          date: new Date(2019, 5, 6),
          title: 'test 5',
          id: 3,
        },
      ];

      fixture.detectChanges();

      const componentElement: Element = fixture.nativeElement;

      const item1: HTMLElement = componentElement.querySelector('.day-cell[day="4"] .indicator[title="test 1"]');

      expect(item1).not.toBeNull();
      expect(item1.style.backgroundColor).toBe('red');

      const item2: HTMLElement = componentElement.querySelector('.day-cell[day="6"] .indicator[title="test 4"]');

      expect(item2).not.toBeNull();
      expect(item2.style.backgroundColor).toBe('red');

      const item3: HTMLElement = componentElement.querySelector('.day-cell[day="6"] .indicator[title="test 5"]');

      expect(item3).not.toBeNull();
      expect(item3.style.backgroundColor).toBe('white');
    });
  });

  describe('calendar navigation', () => {
    it('should trigger changeMonths with 1 when button is clicked ', () => {
      spyOn(fixture.componentInstance, 'changeMonth').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.mode = 'month';

      fixture.detectChanges();

      const componentElement: Element = fixture.nativeElement;

      const buttonNext: HTMLElement = componentElement.querySelector('.header button.next');

      buttonNext.click();

      expect(fixture.componentInstance.changeMonth).toHaveBeenCalledWith(1);
    });

    it('should trigger changeMonths with -1 when button is clicked ', () => {
      spyOn(fixture.componentInstance, 'changeMonth').and.callThrough();

      fixture.componentInstance.year = 2019;
      fixture.componentInstance.month = 6;
      fixture.componentInstance.mode = 'month';

      fixture.detectChanges();

      const componentElement: Element = fixture.nativeElement;

      const buttonBack: HTMLElement = componentElement.querySelector('.header button.back');

      buttonBack.click();

      expect(fixture.componentInstance.changeMonth).toHaveBeenCalledWith(-1);
    });

    it('should trigger changeWeek with 1 when button is clicked', () => {
      spyOn(fixture.componentInstance, 'changeWeek').and.callThrough();
      fixture.componentInstance.year = 2019;
      fixture.componentInstance.week = 23;
      fixture.componentInstance.mode = 'week';

      fixture.detectChanges();
      const componentElement: Element = fixture.nativeElement;

      const buttonNext: HTMLElement = componentElement.querySelector('.header button.next');

      buttonNext.click();
      expect(fixture.componentInstance.changeWeek).toHaveBeenCalledWith(1);
    });

    it('should trigger changeWeek with -1 when button is clicked', () => {
      spyOn(fixture.componentInstance, 'changeWeek').and.callThrough();
      fixture.componentInstance.year = 2019;
      fixture.componentInstance.week = 23;
      fixture.componentInstance.mode = 'week';

      fixture.detectChanges();
      const componentElement: Element = fixture.nativeElement;

      const buttonBack: HTMLElement = componentElement.querySelector('.header button.back');

      buttonBack.click();
      expect(fixture.componentInstance.changeWeek).toHaveBeenCalledWith(-1);
    });
  });
});
