import React from 'react';

import { generateId, isDevMode } from '../utils';

import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
import { View } from './View';
import { ViewItem } from './ViewItem';
import { ViewTransitionManager } from './ViewTransitionManager';

interface StackManagerProps {
  id?: string;
  routeManager: RouteManagerContextState;
  children?: React.ReactNode;
}

interface StackManagerState {
  routerOutletReady: boolean;
}

class StackManagerInner extends React.Component<StackManagerProps, StackManagerState> {
  routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();

  id: string;

  constructor(props: StackManagerProps) {
    super(props);
    this.id = this.props.id || generateId();
    this.handleViewSync = this.handleViewSync.bind(this);
    this.handleHideView = this.handleHideView.bind(this);
    this.state = {
      routerOutletReady: false
    };
  }

  componentDidMount() {
    this.props.routeManager.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!);
    this.routerOutletEl.current!.addEventListener('routerOutletReady', () => {
      this.setState({
        routerOutletReady: true
      });
    });
  }

  static getDerivedStateFromProps(props: StackManagerProps, state: StackManagerState) {
    props.routeManager.syncRoute('', props.children);
    return state;
  }

  componentWillUnmount() {
    this.props.routeManager.removeViewStack(this.id);
  }

  handleViewSync(page: HTMLElement, viewId: string) {
    this.props.routeManager.syncView(page, viewId);
  }

  handleHideView(viewId: string) {
    this.props.routeManager.hideView(viewId);
  }

  renderChild(item: ViewItem, route: any) {
    const component = React.cloneElement(route, {
      computedMatch: item.routeData.match
    });
    return component;
  }

  render() {
    const routeManager = this.props.routeManager;
    const viewStack = routeManager.viewStacks.get(this.id);
    const views = (viewStack || { views: [] }).views.filter(x => x.show);
    const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
    const { routerOutletReady } = this.state;

    const childElements = routerOutletReady ? views.map(view => {
      const route = routeManager.getRoute(view.routeId);
      return (
        <ViewTransitionManager
          id={view.id}
          key={view.key}
          mount={view.mount}
        >
          <View
            onViewSync={this.handleViewSync}
            onHideView={this.handleHideView}
            view={view}
            route={route}
          >
            {this.renderChild(view, route)}
          </View>
        </ViewTransitionManager>
      );
    }) : <div></div>;

    const elementProps: any = {
      ref: this.routerOutletEl
    };

    if(ionRouterOutlet.props.forwardedRef) {
      ionRouterOutlet.props.forwardedRef.current = this.routerOutletEl;
    }

    if (isDevMode()) {
      elementProps['data-stack-id'] = this.id;
    }

    const routerOutletChild = React.cloneElement(ionRouterOutlet, elementProps, childElements);

    return routerOutletChild;
  }
}

const withContext = (Component: any) => {
  return (props: any) => (
    <RouteManagerContext.Consumer>
      {context => <Component {...props} routeManager={context} />}
    </RouteManagerContext.Consumer>
  )
}

export const StackManager = withContext(StackManagerInner);
