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

import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { FormGroup, ReactiveFormsModule, ValidationErrors } from '@angular/forms';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { LsLocation } from '../model/lsLocation';
import { LsPosition } from '../model/lsPosition';
import { GenericLocationFormUIService } from './generic-location-form-u-i.service';

import { GenericLocationFormComponent } from './generic-location-form.component';

describe('GenericLocationFormComponent', () => {
  class LocationCreator {
    static createBuildingLocation(): LsLocation {
      return {
        id: '2',
        name: 'Fe710',
        type: 'building',
        address: {
          street: 'Alte Ziegelei',
          houseNumber: '5',
          postalCode: '88443',
          city: 'Immenstaad',
          countryCode: 'DE',
        },
        geoCoordinate: <LsPosition>{
          longitude: 9.1706,
          latitude: 48.8144,
          altitude: 270,
        },
        boundary: {
          type: 'Polygon',
          coordinates: [
            [
              [9.17029693722725, 48.8142892815782],
              [9.170848665081524, 48.81481394670166],
              [9.171023815870287, 48.81473790438878],
              [9.170382767915726, 48.814135618511386],
              [9.17029693722725, 48.8142892815782],
            ],
          ],
        },
        supplements: {
          key1: 'value 1',
          key2: 'value 2',
        },
      };
    }

    static createFloorLocation(): LsLocation {
      return {
        id: '4',
        name: 'Fe710 EG',
        type: 'floor',
        geoCoordinate: <LsPosition>{
          longitude: 9.17055,
          latitude: -90,
          altitude: 270,
        },
        boundary: {
          type: 'Polygon',
          coordinates: [
            [
              [9.17029693722725, 48.8142892815782],
              [9.170848665081524, 48.81481394670166],
              [9.171023815870287, 48.81473790438878],
              [9.170382767915726, 48.814135618511386],
              [9.17029693722725, 48.8142892815782],
            ],
          ],
        },
      };
    }
  }

  class GeoCoordinateValidator {
    static checkExpectedErrors(form: FormGroup, coordinate: string, expectedErrors: ValidationErrors): void {
      const coordinates = form.controls['geoCoordinate'] as FormGroup;
      expect(form.valid).toBeFalsy();
      const actualErrors = coordinates.controls[coordinate].errors || {};
      expect(expectedErrors).toEqual(actualErrors);
    }

    static checkExpectedNoErrors(form: FormGroup, coordinate: string): void {
      const coordinates = form.controls['geoCoordinate'] as FormGroup;
      const actualErrors = coordinates.controls[coordinate].errors || {};
      expect({}).toEqual(actualErrors);
    }
  }

  let component: GenericLocationFormComponent;
  let fixture: ComponentFixture<GenericLocationFormComponent>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        imports: [NoopAnimationsModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, MatExpansionModule],
        providers: [GenericLocationFormUIService],
        declarations: [GenericLocationFormComponent],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(GenericLocationFormComponent);
    component = fixture.componentInstance;
    // component.ngOnInit();
    fixture.detectChanges();
  });

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

  it('should emit location clone with form values applied', () => {
    const locationBuilding: LsLocation = LocationCreator.createBuildingLocation();

    component.location = locationBuilding;

    let editedLocation: LsLocation = null;
    component.save.subscribe((location) => {
      editedLocation = location;
    });

    // get control of 'name' attribute and change it's value
    const nameControl = component.form.controls['name'];
    nameControl.setValue('changed name');

    // get control of 'street' attribute and change it's value
    const streetControl = (component.form.controls['address'] as FormGroup).controls['street'];
    streetControl.setValue('changed street');

    // save should emit a clone
    component.triggerSave();

    const errorsName = nameControl.errors || {};
    expect(errorsName['required']).toBeFalsy();

    const errorsStreet = streetControl.errors || {};
    expect(errorsStreet['required']).toBeFalsy();

    // expecting different name
    expect(locationBuilding.name).not.toEqual(editedLocation.name);
    expect(locationBuilding.address.street).not.toEqual(editedLocation.address.street);
  });

  it('should clear form when location is set to undefined', () => {
    component.location = undefined;

    let changedLocation: LsLocation = null;
    component.save.subscribe((location) => {
      changedLocation = location;
    });

    expect(component.form.controls['name'].value).toBeNull();
    expect(component.form.controls['type'].value).toBeNull();

    expect(changedLocation).toBeNull();
  });

  it('should build and intialize form when minimal location is set', () => {
    component.location = {
      id: '2',
      name: 'Fe710',
      type: 'building',
    };

    expect(component.form.controls['name'].value).toEqual('Fe710');
    expect(component.form.controls['type'].value).toEqual('building');
    expect((component.form.controls['address'] as FormGroup).controls['street'].value).toBeNull();
  });

  it('should build and intialize form when location is set', () => {
    const locationBuilding: LsLocation = LocationCreator.createBuildingLocation();
    component.location = locationBuilding;

    /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
    let notChangedLocation: LsLocation = locationBuilding;

    component.save.subscribe((location) => {
      notChangedLocation = location;
    });

    expect(component.form.controls['name'].value).toEqual('Fe710');
    expect(component.form.controls['type'].value).toEqual('building');
    expect((component.form.controls['address'] as FormGroup).controls['street'].value).toEqual('Alte Ziegelei');
    expect((component.form.controls['supplements'] as FormGroup).controls['key1'].value).toEqual('value 1');
  });

  it('should build and update form when location is changed', () => {
    component.location = LocationCreator.createBuildingLocation();
    expect(component.form.controls['name'].value).toEqual('Fe710');
    expect((component.form.controls['address'] as FormGroup).controls['street'].value).toEqual('Alte Ziegelei');
    expect((component.form.controls['supplements'] as FormGroup).controls['key1'].value).toEqual('value 1');

    // now change the location and expect to see these values in the form
    component.location = LocationCreator.createFloorLocation();
    expect(component.form.controls['name'].value).toEqual('Fe710 EG');
    expect((component.form.controls['address'] as FormGroup).controls['street'].value).toBeNull();
    expect((component.form.controls['supplements'] as FormGroup).controls).toEqual({});
  });

  describe('coordinate validation', () => {
    it('should support longitude from -180 to +180', () => {
      const locationFloor: LsLocation = LocationCreator.createFloorLocation();

      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedNoErrors(component.form, 'longitude');

      locationFloor.geoCoordinate.longitude = 180;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedNoErrors(component.form, 'longitude');

      locationFloor.geoCoordinate.longitude = -181;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedErrors(component.form, 'longitude', { min: { min: -180, actual: -181 } });

      locationFloor.geoCoordinate.longitude = 181;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedErrors(component.form, 'longitude', { max: { max: 180, actual: 181 } });
    });

    it('should support latitude from -90 to +90', () => {
      const locationFloor: LsLocation = LocationCreator.createFloorLocation();

      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedNoErrors(component.form, 'latitude');

      locationFloor.geoCoordinate.latitude = 90;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedNoErrors(component.form, 'latitude');

      locationFloor.geoCoordinate.latitude = -91;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedErrors(component.form, 'latitude', { min: { min: -90, actual: -91 } });

      locationFloor.geoCoordinate.latitude = +91;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedErrors(component.form, 'latitude', { max: { max: 90, actual: 91 } });
    });

    it('should support altitude from -1000000 to +1000000', () => {
      const locationFloor: LsLocation = LocationCreator.createFloorLocation();

      locationFloor.geoCoordinate.altitude = -1000000.001;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedNoErrors(component.form, 'altitude');

      locationFloor.geoCoordinate.altitude = 1000000.001;
      component.location = locationFloor;
      GeoCoordinateValidator.checkExpectedNoErrors(component.form, 'altitude');
    });
  });

  // it('should set boundary and and mark dirty', () => {
  //   fail();
  // });
  //
  // it('should set position and and mark dirty without altering z-index', () => {
  //   fail();
  // });
  //
  // it('should set position and and mark dirty without no z-index', () => {
  //   fail();
  // });
  //
  // it('should not contain boundary without coordinates', () => {
  //   fail();
  // });
  //
  // it('should have boundary with correct type', () => {
  //   fail();
  // });
});
