// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {dingus} from 'dingusjs';

import {Actions, DeferredAction} from '../common/actions';

import {Router} from './router';

const mockComponent = {
  view() {}
};

const fakeDispatch = () => {};

beforeEach(() => {
  window.onhashchange = null;
  window.location.hash = '';
});

test('Default route must be defined', () => {
  expect(() => new Router('/a', {'/b': mockComponent}, fakeDispatch)).toThrow();
});

test('Resolves empty route to default component', () => {
  const router = new Router('/a', {'/a': mockComponent}, fakeDispatch);
  expect(router.resolve('')).toBe(mockComponent);
  expect(router.resolve(null)).toBe(mockComponent);
});

test('Parse route from hash', () => {
  const router = new Router('/', {'/': mockComponent}, fakeDispatch);
  window.location.hash = '#!/foobar?s=42';
  expect(router.getRouteFromHash()).toBe('/foobar');

  window.location.hash = '/foobar';  // Invalid prefix.
  expect(router.getRouteFromHash()).toBe('');
});

test('Set valid route on hash', () => {
  const dispatch = dingus<(a: DeferredAction) => void>();
  const router = new Router(
      '/',
      {
        '/': mockComponent,
        '/a': mockComponent,
      },
      dispatch);
  const prevHistoryLength = window.history.length;

  router.setRouteOnHash('/a');
  expect(window.location.hash).toBe('#!/a');
  expect(window.history.length).toBe(prevHistoryLength + 1);
  // No navigation action should be dispatched.
  expect(dispatch.calls.length).toBe(0);
});

test('Redirects to default for invalid route in setRouteOnHash ', () => {
  const dispatch = dingus<(a: DeferredAction) => void>();
  // const dispatch = () => {console.log("action received")};

  const router = new Router('/', {'/': mockComponent}, dispatch);
  router.setRouteOnHash('foo');
  expect(dispatch.calls.length).toBe(1);
  expect(dispatch.calls[0][1].length).toBeGreaterThanOrEqual(1);
  expect(dispatch.calls[0][1][0]).toEqual(Actions.navigate({route: '/'}));
});

test('Navigate on hash change', done => {
  const mockDispatch = (a: DeferredAction) => {
    expect(a).toEqual(Actions.navigate({route: '/viewer'}));
    done();
  };
  new Router(
      '/',
      {
        '/': mockComponent,
        '/viewer': mockComponent,
      },
      mockDispatch);
  window.location.hash = '#!/viewer';
});

test('Redirects to default when invalid route set in window location', done => {
  const mockDispatch = (a: DeferredAction) => {
    expect(a).toEqual(Actions.navigate({route: '/'}));
    done();
  };

  new Router(
      '/',
      {
        '/': mockComponent,
        '/viewer': mockComponent,
      },
      mockDispatch);

  window.location.hash = '#invalid';
});

test('navigateToCurrentHash with valid current route', () => {
  const dispatch = dingus<(a: DeferredAction) => void>();
  window.location.hash = '#!/b';
  const router =
      new Router('/', {'/': mockComponent, '/b': mockComponent}, dispatch);
  router.navigateToCurrentHash();
  expect(dispatch.calls[0][1][0]).toEqual(Actions.navigate({route: '/b'}));
});

test('navigateToCurrentHash with invalid current route', () => {
  const dispatch = dingus<(a: DeferredAction) => void>();
  window.location.hash = '#!/invalid';
  const router = new Router('/', {'/': mockComponent}, dispatch);
  router.navigateToCurrentHash();
  expect(dispatch.calls[0][1][0]).toEqual(Actions.navigate({route: '/'}));
});

test('Params parsing', () => {
  window.location.hash = '#!/foo?a=123&b=42&c=a?b?c';
  const router = new Router('/', {'/': mockComponent}, fakeDispatch);
  expect(router.param('a')).toBe('123');
  expect(router.param('b')).toBe('42');
  expect(router.param('c')).toBe('a?b?c');
});
