/**
 * @jest-environment jsdom
 */

import { Router } from './Router';
import { Page } from './Page';
import { Route } from './Route';
import { dangerousResetGlobalRouterUseForTestOnly } from '../methods';
import { HistoryUpdateType, RouterGenericBase } from './Types';

type PanelIdType = 'panel_main' | 'user' | 'info' | 'done' | 'main' | 'create';
type ViewIdType = 'main' | 'create';
type PageIdType = '/' | '/user' | '/info' | '/done' | '/create' | '/product';

interface RouterTypes extends RouterGenericBase {
  panelId: PanelIdType;
  viewId: ViewIdType;
  pageId: PageIdType;
}

function delay(time = 100) {
  return new Promise((resolve) => setTimeout(resolve, time));
}

test('route basic usage', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router<RouterTypes>(
    {
      '/': new Page<RouterTypes>(),
      '/user': new Page<RouterTypes>('user'),
      '/info': new Page<RouterTypes>('info'),
    },
    null
  );

  const recordEvents: Array<{
    newRoute: Route;
    oldRoute: Route | undefined;
    isNewRoute: boolean;
    type: HistoryUpdateType;
  }> = [];

  r.on('update', (newRoute, oldRoute, isNewRoute, type) => {
    recordEvents.push({ newRoute, oldRoute, isNewRoute, type });
  });

  r.start();
  expect(r.history.getCurrentIndex()).toBe(0);
  r.pushPage('/user');
  expect(r.history.getCurrentIndex()).toBe(1);
  await delay(10);
  r.pushPage('/info');
  expect(r.history.getCurrentIndex()).toBe(2);
  await delay(10);
  r.popPage();
  await delay(10);
  expect(r.history.getCurrentIndex()).toBe(1);
  r.replacePage('/info');
  await delay(10);
  expect(r.history.getCurrentIndex()).toBe(1);

  setTimeout(() => {
    // одно событие update приходит после старта роутера
    expect(recordEvents.length).toBe(4 + 1);
    expect(recordEvents[0].isNewRoute).toBeTruthy();
    expect(recordEvents[1].isNewRoute).toBeTruthy();
    expect(recordEvents[2].isNewRoute).toBeTruthy();
    expect(recordEvents[3].isNewRoute).toBeFalsy();
    expect(recordEvents[4].isNewRoute).toBeTruthy();
    done();
    r.stop();
  }, 10);
});

test('route first page push', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router<RouterTypes>(
    {
      '/': new Page<RouterTypes>(),
      '/user': new Page<RouterTypes>('user'),
      '/info': new Page<RouterTypes>('info'),
    },
    null
  );

  r.start();
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(true); // После старта страница всегда first
  r.pushPage('/info');
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(false); // После push страница всегда не будет first
  r.popPage();
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(true); // Вернулись назад на первую страницу, она была и осталась first
  r.stop();
  done();
});

test('route first page replace', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router<RouterTypes>(
    {
      '/': new Page<RouterTypes>(),
      '/user': new Page<RouterTypes>('user'),
      '/info': new Page<RouterTypes>('info'),
    },
    null
  );

  r.start();
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(true); // После старта страница всегда first
  r.replacePage('/info');
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(true); // Мы заменили first страницу на какую-то другу, она осталась first
  r.pushPage('/user');
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(false); // Был push страница уже не first
  r.popPage();
  await delay(100);
  expect(r.getCurrentLocation().isFirstPage()).toBe(true); // Вернулись обратно на first страницу
  r.stop();
  done();
});

test('check history', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router<RouterTypes>(
    {
      '/': new Page<RouterTypes>('main', 'main'),
      '/user': new Page<RouterTypes>('user', 'main'),
      '/info': new Page<RouterTypes>('info', 'main'),
      '/create': new Page<RouterTypes>('create', 'create'),
      '/done': new Page('done', 'create'),
    },
    null
  );

  r.start();

  expect(r.getCurrentLocation().getViewHistory('main')).toEqual(['main']);
  expect(r.getCurrentLocation().getPanelId()).toBe('main');
  expect(r.getCurrentLocation().getViewId()).toBe('main');
  r.pushPage('/user');
  expect(r.getCurrentLocation().getViewHistory('main')).toEqual(['main', 'user']);
  expect(r.getCurrentLocation().getPanelId()).toBe('user');
  expect(r.getCurrentLocation().getViewId()).toBe('main');
  r.pushPage('/info');
  expect(r.getCurrentLocation().getViewHistory('main')).toEqual(['main', 'user', 'info']);
  expect(r.getCurrentLocation().getPanelId()).toBe('info');
  expect(r.getCurrentLocation().getViewId()).toBe('main');
  r.pushPage('/create');
  expect(r.getCurrentLocation().getViewHistory('main')).toEqual(['info']);
  expect(r.getCurrentLocation().getViewHistory('create')).toEqual(['create']);
  expect(r.getCurrentLocation().getPanelId()).toBe('create');
  expect(r.getCurrentLocation().getViewId()).toBe('create');
  r.pushPage('/done');
  expect(r.getCurrentLocation().getViewHistory('create')).toEqual(['create', 'done']);
  expect(r.getCurrentLocation().getViewHistory('main')).toEqual(['info']);
  expect(r.getCurrentLocation().getPanelId()).toBe('done');
  expect(r.getCurrentLocation().getViewId()).toBe('create');
  expect(r.getCurrentLocation().getLastPanelInView('main')).toBe('info');
  r.popPage();
  await delay(100);
  expect(r.getCurrentLocation().getViewHistory('create')).toEqual(['create']);
  expect(r.getCurrentLocation().getLastPanelInView('main')).toBe('info');

  r.stop();
  done();
});

test('route preventSameLocationChange', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router<RouterTypes>(
    {
      '/': new Page(),
      '/user': new Page('user'),
      '/info': new Page<RouterTypes>('info'),
    },
    { preventSameLocationChange: true }
  );

  r.start();
  expect(r.history.getCurrentIndex()).toBe(0);
  r.pushPage('/user');
  expect(r.history.getCurrentIndex()).toBe(1);
  await delay(10);

  // Пушим точно такую же страницу, ничего не должно произойти
  let wasUpdates = false;
  r.once('update', () => (wasUpdates = true));
  r.pushPage('/user');
  expect(r.history.getCurrentIndex()).toBe(1);
  expect(wasUpdates).toBe(false);
  done();
});

test('fixBrokenHistory', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router({
    '/': new Page(),
    '/user': new Page('user'),
    '/product': new Page<RouterTypes>('panel_main'),
  });
  r.start();
  r.pushPage('/user');
  r.pushPage('/product');

  // Эмитируем открытие м закрытие левого приложения
  window.history.pushState(null, 'OTHER PAGE OPEN', '#PAGE OPEN');

  r.fixBrokenHistory();
  expect(r.getCurrentLocation().getPageId()).toBe('/product');
  r.popPage();
  await r.afterUpdate();
  expect(r.getCurrentLocation().getPageId()).toBe('/user');
  done();
});

test('popPageToAndReplace', async (done) => {
  dangerousResetGlobalRouterUseForTestOnly();
  const r = new Router<RouterTypes>({
    '/': new Page<RouterTypes>(),
    '/user': new Page('user'),
    '/product': new Page<RouterTypes>('user'),
    '/done': new Page<RouterTypes>('done'),
  });
  r.start();
  r.pushPage('/user');
  r.pushPage('/product');

  // r.popPageToAndReplace(-2, '/done', {});
  // await r.afterUpdate();

  // expect(r.history.getCurrentIndex()).toBe(0);
  await done();
});

test('it should use certain parameter as hash', () => {
  dangerousResetGlobalRouterUseForTestOnly();
  window.history.pushState(null, '', `http://localhost?app_hash=${btoa('user')}&hash=done#product`);

  const r = new Router<RouterTypes>({
    '/': new Page<RouterTypes>(),
    '/user': new Page('user'),
    '/product': new Page<RouterTypes>('user'),
    '/done': new Page<RouterTypes>('done'),
  });
  r.start('app_hash');

  expect(r.getCurrentLocation().getPageId()).toBe('/user');
});

test('it should use modal id from query parameter', () => {
  dangerousResetGlobalRouterUseForTestOnly();
  window.history.pushState(
    null,
    '',
    `http://localhost?app_hash=${btoa('?m=modal_features_intro&feature_key=museum_intro')}&hash=done#product`
  );

  const r = new Router<RouterTypes>({
    '/': new Page<RouterTypes>(),
    '/user': new Page('user'),
    '/product': new Page<RouterTypes>('user'),
    '/done': new Page<RouterTypes>('done'),
  });
  r.start('app_hash');

  expect(r.getCurrentLocation().getModalId()).toBe('modal_features_intro');
});
