import {async, TestBed} from '@angular/core/testing';
import { ApiService } from './api.service';
import {HttpClient} from '@angular/common/http';
import {LocalStorageService} from 'angular-2-local-storage';
import {Observable} from 'rxjs';
import {ModalService} from '@inst-iot/bosch-angular-ui-components';

let apiService: ApiService;
let httpClientSpy: jasmine.SpyObj<HttpClient>;
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
let modalServiceSpy: jasmine.SpyObj<ModalService>;
let windowServiceSpy: jasmine.SpyObj<Window>;

// TODO
// TODO: test options

describe('ApiService', () => {
  beforeEach(() => {
    const httpSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']);
    const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['get']);
    const modalSpy = jasmine.createSpyObj('ModalService', ['openComponent']);
    const windowSpy = jasmine.createSpyObj('Window', ['location']);

    TestBed.configureTestingModule({
      providers: [
        ApiService,
        {provide: HttpClient, useValue: httpSpy},
        {provide: LocalStorageService, useValue: localStorageSpy},
        {provide: ModalService, useValue: modalSpy},
        {provide: Window, useValue: windowSpy}
      ]
    });

    apiService = TestBed.inject(ApiService);
    httpClientSpy = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>;
    localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
    modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
    windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
  });

  it('should be created', () => {
    expect(apiService).toBeTruthy();
  });

  it('shows an error message when the request fails', () => {
    const getReturn = new Observable(observer => {
      observer.error('error');
    });
    httpClientSpy.get.and.returnValue(getReturn);
    localStorageServiceSpy.get.and.returnValue(undefined);
    modalServiceSpy.openComponent.and.returnValue({instance: {message: ''}} as any);

    apiService.get('/testurl');
    expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
    expect(modalServiceSpy.openComponent.calls.count()).toBe(1);
  });

  it('returns the error message if the callback function had an error parameter', () => {
    const getReturn = new Observable(observer => {
      observer.error('error');
    });
    httpClientSpy.get.and.returnValue(getReturn);
    localStorageServiceSpy.get.and.returnValue(undefined);
    modalServiceSpy.openComponent.and.returnValue({instance: {message: ''}} as any);

    apiService.get('/testurl', (data, error) => {
      expect(modalServiceSpy.openComponent.calls.count()).toBe(0);
      expect(error).toBe('error');
    });
  });

  it('should do get requests without auth if not available', async(() => {
    const getReturn = new Observable(observer => {
      observer.next({body: 'data', headers: {keys: () => []}});
    });
    httpClientSpy.get.and.returnValue(getReturn);
    localStorageServiceSpy.get.and.returnValue(undefined);

    apiService.get('/testurl', res => {
      expect(res).toBe('data');
      expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
    });
  }));

  it('should do get requests with basic auth if available', async(() => {
    const getReturn = new Observable(observer => {
      observer.next('data');
    });
    httpClientSpy.get.and.returnValue(getReturn);
    localStorageServiceSpy.get.and.returnValue('basicAuth');

    apiService.get('/testurl', res => {
      expect(res).toBe('data');
      expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
    });
  }));

  it('should do post requests', async(() => {
    const resReturn = new Observable(observer => {
      observer.next('data');
    });
    httpClientSpy.post.and.returnValue(resReturn);
    localStorageServiceSpy.get.and.returnValue('basicAuth');

    apiService.post('/testurl', 'reqData', res => {
      expect(res).toBe('data');
      expect(httpClientSpy.post).toHaveBeenCalledWith('/api/testurl', 'reqData', jasmine.any(Object));
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
    });
  }));

  it('should do put requests', async(() => {
    const resReturn = new Observable(observer => {
      observer.next('data');
    });
    httpClientSpy.put.and.returnValue(resReturn);
    localStorageServiceSpy.get.and.returnValue('basicAuth');

    apiService.put('/testurl', 'reqData', res => {
      expect(res).toBe('data');
      expect(httpClientSpy.put).toHaveBeenCalledWith('/api/testurl', 'reqData', jasmine.any(Object));
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
    });
  }));

  it('should do delete requests', async(() => {
    const resReturn = new Observable(observer => {
      observer.next('data');
    });
    httpClientSpy.delete.and.returnValue(resReturn);
    localStorageServiceSpy.get.and.returnValue('basicAuth');

    apiService.delete('/testurl', res => {
      expect(res).toBe('data');
      expect(httpClientSpy.delete).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
    });
  }));

  // TODO: test return headers
});
