import React from 'react';
import { Redirect, Route, useHistory, RouteProps, RouteComponentProps, match as MatchType } from 'react-router-dom';

import { Layout } from 'soapbox/components/ui';
import { useOwnAccount, useSettings } from 'soapbox/hooks';

import BundleColumnError from '../components/bundle_column_error';
import ColumnForbidden from '../components/column_forbidden';
import ColumnLoading from '../components/column_loading';
import ColumnsArea from '../components/columns_area';
import BundleContainer from '../containers/bundle_container';

type PageProps = {
  params?: MatchType['params'],
  layout?: any,
  children: React.ReactNode,
};

interface IWrappedRoute extends RouteProps {
  component: (...args: any[]) => any,
  page?: React.ComponentType<PageProps>,
  content?: React.ReactNode,
  componentParams?: Record<string, any>,
  layout?: any,
  publicRoute?: boolean,
  staffOnly?: boolean,
  adminOnly?: boolean,
  developerOnly?: boolean,
}

const WrappedRoute: React.FC<IWrappedRoute> = ({
  component,
  page: Page,
  content,
  componentParams = {},
  layout,
  publicRoute = false,
  staffOnly = false,
  adminOnly = false,
  developerOnly = false,
  ...rest
}) => {
  const history = useHistory();

  const account = useOwnAccount();
  const settings = useSettings();

  const renderComponent = ({ match }: RouteComponentProps) => {
    if (Page) {
      return (
        <BundleContainer fetchComponent={component} loading={renderLoading} error={renderError}>
          {Component =>
            (
              <Page params={match.params} layout={layout} {...componentParams}>
                <Component params={match.params} {...componentParams}>
                  {content}
                </Component>
              </Page>
            )
          }
        </BundleContainer>
      );
    }

    return (
      <BundleContainer fetchComponent={component} loading={renderLoading} error={renderError}>
        {Component =>
          (
            <ColumnsArea layout={layout}>
              <Component params={match.params} {...componentParams}>
                {content}
              </Component>
            </ColumnsArea>
          )
        }
      </BundleContainer>
    );
  };

  const renderWithLayout = (children: JSX.Element) => (
    <>
      <Layout.Main>
        {children}
      </Layout.Main>

      <Layout.Aside />
    </>
  );

  const renderLoading = () => renderWithLayout(<ColumnLoading />);
  const renderForbidden = () => renderWithLayout(<ColumnForbidden />);
  const renderError = (props: any) => renderWithLayout(<BundleColumnError {...props} />);

  const loginRedirect = () => {
    const actualUrl = encodeURIComponent(`${history.location.pathname}${history.location.search}`);
    localStorage.setItem('soapbox:redirect_uri', actualUrl);
    return <Redirect to='/login' />;
  };

  const authorized = [
    account || publicRoute,
    developerOnly ? settings.get('isDeveloper') : true,
    staffOnly ? account && account.staff : true,
    adminOnly ? account && account.admin : true,
  ].every(c => c);

  if (!authorized) {
    if (!account) {
      return loginRedirect();
    } else {
      return renderForbidden();
    }
  }

  return <Route {...rest} render={renderComponent} />;
};

export {
  WrappedRoute,
};
