/*
 * Copyright (C) 2019 Robert Bosch GmbH Copyright (C) 2019 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

/* tslint:disable:no-magic-numbers */

import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ColumnBrowserComponent } from './column-browser.component';
import { BciColumnBrowserModel, Column, ColumnItem, SelectionChange } from './column-browser.model';
import { ColumnBrowserService } from './column-browser.service';

const data: BciColumnBrowserModel[] = [
  { label: 'dummy1', children: [], model: 'dummy1' },
  {
    label: 'dummy2',
    children: [
      { label: 'dummy21', children: [], model: 'dummy21' },
      {
        label: 'dummy22',
        children: [{ label: 'dummy221', children: [], model: 'dummy221' }],
        model: 'dummy22',
      },
    ],
    model: 'dummy2',
  },
];

const source = [
  new Column('', [
    new ColumnItem(data[0], undefined, data[0].label, false, true, false),
    new ColumnItem(data[1], undefined, data[1].label, false, true, true),
  ]),
  new Column('', [
    new ColumnItem(data[1].children[0], undefined, data[1].children[0].label, false, false, false),
    new ColumnItem(data[1].children[1], undefined, data[1].children[1].label, false, false, true),
  ]),
  new Column('', [
    new ColumnItem(
      data[1].children[1].children[0],
      undefined,
      data[1].children[1].children[0].label,
      false,
      false,
      false
    ),
  ]),
];

source[1].items[0].parentItem = source[0].items[1];
source[1].items[1].parentItem = source[0].items[1];
source[2].items[0].parentItem = source[1].items[1];

describe('ColumnBrowserComponent', () => {
  let component: ColumnBrowserComponent;
  let fixture: ComponentFixture<ColumnBrowserComponent>;
  let columnService: ColumnBrowserService;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [ColumnBrowserComponent],
        providers: [],
        schemas: [NO_ERRORS_SCHEMA],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(ColumnBrowserComponent);
    component = fixture.componentInstance;
    component.dataSource = data;
    fixture.detectChanges();

    columnService = fixture.debugElement.injector.get(ColumnBrowserService);
    jest.spyOn(columnService, 'initColumns');
    jest.spyOn(columnService, 'updateColumns');
    jest.spyOn(columnService as any, 'prepareChildren');
    jest.spyOn(columnService, 'resetColumns');

    jest.spyOn(component.selectedPathChange, 'emit');
    jest.spyOn(component.selectionChange, 'emit');
  });

  it('should create component', () => {
    expect(component).toBeDefined();
  });

  it('ngOnInit', () => {
    // tslint:disable-next-line:no-lifecycle-call
    component.ngOnInit();

    expect(component.visibleColumns.length).toBe(1);
  });

  describe('onItemClick', () => {
    beforeEach(() => {
      jest.spyOn(component as any, 'updateSelectedPath');
    });

    [
      {
        description: 'node is not selected',
        level: 0,
        item: source[0].items[0],
        selected: false,
        selectedPath: ['dummy1'],
        parentItem: null,
      },
      {
        description: 'node is selected',
        level: 0,
        item: source[0].items[0],
        selected: true,
        selectedPath: [],
        parentItem: null,
      },
    ].forEach((config) => {
      it(`onItemClick - ${config.description}`, () => {
        const clickedItem = config.item;
        clickedItem.selected = config.selected;
        component.onItemClick(clickedItem, config.level);

        expect(component['updateSelectedPath']).toHaveBeenCalledTimes(1);
        if (config.item.selected) {
          expect(columnService.updateColumns).not.toHaveBeenCalled();
        } else {
          expect(columnService.updateColumns).toHaveBeenCalledWith(clickedItem, config.level, true);
        }

        const expectedSelection: SelectionChange = {
          columnIndex: config.level,
          selected: !config.selected,
          item: clickedItem.item,
          selectedPath: config.selectedPath,
        };
        expect(component.selectionChange.emit).toHaveBeenCalledWith(expectedSelection);
      });
    });
  });

  [
    { description: 'hit <ENTER>', level: 0 },
    { description: 'hit anything else', level: 0 },
  ].forEach((config) => {
    it(`onKeyDown - ${config.description}`, () => {
      // TODO: test is missing
    });
  });

  [
    { description: 'root without children', item: source[0].items[0], level: 0, result: 1 },
    { description: 'root with children', item: source[0].items[1], level: 0, result: 2 },
    { description: 'child without children', item: source[1].items[0], level: 1, result: 1 },
    { description: 'child with children', item: source[1].items[1], level: 1, result: 2 },
  ].forEach((config) => {
    it(`visibleColumns & availableItems - ${config.description}`, () => {
      component.onItemClick(config.item, config.level);

      expect(component.visibleColumns.length).toBe(config.result);
    });
  });

  [
    {
      description: 'root column selected w/o children',
      item: source[0].items[0],
      parentItem: null,
      level: 0,
      result: [],
    },
    {
      description: 'root column selected with children',
      item: source[0].items[1],
      parentItem: null,
      level: 0,
      result: ['dummy21', 'dummy22'],
    },
    {
      description: 'child column selected with children',
      item: source[1].items[1],
      parentItem: null,
      level: 1,
      result: ['dummy221'],
    },
  ].forEach((config) => {
    it(`availableItems -  ${config.description}`, () => {
      component.onItemClick(config.item, config.level);
      const items = component.getAvailableItems(config.level + 1).map((item) => item.label);
      expect(items).toEqual(config.result);
    });
  });

  [
    { description: 'level = 0', columnIndex: 0, result: 2 },
    { description: 'level = 1', columnIndex: 0, result: 2 },
    { description: 'level = 100 - invalid', columnIndex: 100, result: -1 },
  ].forEach((config) => {
    it(`getColumnItemCount - ${config.description}`, () => {
      const result = component.getColumnItemCount(config.columnIndex);

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

  it('resetColumns', () => {
    component.resetColumns(0);
    expect(columnService.resetColumns).toHaveBeenCalledWith(0);
    component.resetColumns(1);
    expect(columnService.resetColumns).toHaveBeenCalledWith(1);
  });
});
