import History from './History';
import Route from './Route';

import type { RouteList, RouteParams, RouterCache, Popout, Modal, Page, HistoryEvent } from '../types';

import { log } from '../utils/report';

class Router {
  private static _name = '[Router]';

  cache!: RouterCache;
  history!: History;

  constructor(routes: RouteList) {
    this.cache = new Map();

    this.history = new History(routes);
    this.history.on('update', this._saveCache.bind(this));
    this.history.on('reload', this._replaceFromLocation.bind(this));
  }

  private _saveCache(event: HistoryEvent) {
    log(Router._name, 'Auto save cache.');

    this.cache.set(event.next.view, event.next.panel);
    this.cache.set(event.prev.view, event.prev.panel);
  }

  private _replaceFromLocation() {
    log(Router._name, 'Auto replace from location.');

    const nextRoute = Route.buildFromLocation(this.history.routes, this.history.location);
    this.history.replace(nextRoute);
  }

  pushPage(page: Page, params: RouteParams = {}) {
    log(Router._name, 'Call pushPage with', page);

    const nextRoute = Route.buildFromPage(this.history.routes, page, params);
    this.history.push(nextRoute);
  }

  replacePage(page: Page, params: RouteParams = {}) {
    log(Router._name, 'Call replacePage with', page);

    const nextRoute = Route.buildFromPage(this.history.routes, page, params);
    this.history.replace(nextRoute);
  }

  popPage() {
    log(Router._name, 'Call popPage.');

    this.history.back();
  }

  pushPageAfterMove(prevPage: Page, nextPage: Page, params: RouteParams = {}) {
    log(Router._name, 'Call pushPageAfterMove from', prevPage, 'to', nextPage);

    const prevRoute = Route.buildFromPage(this.history.routes, prevPage);
    const nextRoute = Route.buildFromPage(this.history.routes, nextPage, params);
    this.history.pushAfterMove(prevRoute, nextRoute);
  }

  pushModal(modal: Modal, params: RouteParams = {}) {
    log(Router._name, 'Call pushModal with', modal);

    const currentRoute = this.history.route;
    const nextRoute = currentRoute.clone();
    nextRoute.modal = modal;
    nextRoute.params = params;
    nextRoute.compile(currentRoute.page);
    this.history.push(nextRoute);
  }

  replaceModal(modal: Modal, params: RouteParams = {}) {
    log(Router._name, 'Call replaceModal with', modal);

    const currentRoute = this.history.route;
    const nextRoute = currentRoute.clone();
    nextRoute.modal = modal;
    nextRoute.params = params;
    nextRoute.compile(currentRoute.page);
    if (currentRoute.hasModal) {
      this.history.replace(nextRoute);
    } else {
      this.history.push(nextRoute);
    }
  }

  pushPopup(popup: Popout, params: RouteParams = {}) {
    log(Router._name, 'Call pushPopup with', popup);

    const currentRoute = this.history.route;
    const nextRoute = currentRoute.clone();
    nextRoute.popout = popup;
    nextRoute.params = params;
    nextRoute.compile(currentRoute.page);
    this.history.push(nextRoute);
  }

  replacePopup(popup: Popout, params: RouteParams = {}) {
    log(Router._name, 'Call replacePopup with', popup);

    const currentRoute = this.history.route;
    const nextRoute = currentRoute.clone();
    nextRoute.popout = popup;
    nextRoute.params = params;
    nextRoute.compile(currentRoute.page);
    if (currentRoute.hasPopout) {
      this.history.replace(nextRoute);
    } else {
      this.history.push(nextRoute);
    }
  }

  popPageIfModal() {
    log(Router._name, 'Call popPageIfModal.');

    const currentRoute = this.history.route;
    if (currentRoute.hasModal) {
      this.history.back();
    } else {
      log(Router._name, 'No modal was found.');
    }
  }

  popPageIfPopup() {
    log(Router._name, 'Call popPageIfPopup.');

    const currentRoute = this.history.route;
    if (currentRoute.hasPopout) {
      this.history.back();
    } else {
      log(Router._name, 'No popout was found.');
    }
  }

  popPageIfModalOrPopup() {
    log(Router._name, 'Call popPageIfModalOrPopup.');

    const currentRoute = this.history.route;
    if (currentRoute.hasModal || currentRoute.hasPopout) {
      this.history.back();
    } else {
      log(Router._name, 'No modal or popup was found.');
    }
  }

  canMoveTo(page: Page, params: RouteParams = {}) {
    const route = Route.buildFromPage(this.history.routes, page, params);
    const index = this.history.lastIndexOf(route);
    return this.history.canMoveTo(index);
  }

  moveTo(page: Page, params: RouteParams = {}) {
    log(Router._name, 'Call moveTo with', page);

    const route = Route.buildFromPage(this.history.routes, page, params);
    const index = this.history.lastIndexOf(route);
    this.history.moveTo(index);
  }
}

export { Router };
export default Router;
