import * as _ from 'lodash-es';
import * as React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';

import { useDynamicPluginInfo } from '@console/plugin-sdk/src/api/useDynamicPluginInfo';
import {
  FLAGS,
  useActivePerspective,
  useUserSettings,
  getPerspectiveVisitedKey,
} from '@console/shared';
import { connectToFlags } from '../reducers/connectToFlags';
import { flagPending, FlagsObject } from '../reducers/features';
import { GlobalNotifications } from './global-notifications';
import { NamespaceBar } from './namespace';
import { SearchPage } from './search';
import { ResourceDetailsPage, ResourceListPage } from './resource-list';
import { AsyncComponent, LoadingBox } from './utils';
import { namespacedPrefixes } from './utils/link';
import { AlertmanagerModel, VolumeSnapshotModel } from '../models';
import { referenceForModel } from '../module/k8s';
import { NamespaceRedirect } from './utils/namespace-redirect';

//PF4 Imports
import { PageSection, PageSectionVariants } from '@patternfly/react-core';
import { RoutePage, isRoutePage, useExtensions, LoadedExtension } from '@console/plugin-sdk';
import {
  Perspective,
  isPerspective,
  RoutePage as DynamicRoutePage,
  isRoutePage as isDynamicRoutePage,
} from '@console/dynamic-plugin-sdk';
import CreateResource from './create-resource';

const RedirectComponent = (props) => {
  const to = `/k8s${props.location.pathname}`;
  return <Redirect to={to} />;
};

// Ensure a *const* function wrapper for each namespaced Component so that react router doesn't recreate them
const Memoized = new Map();
function NamespaceFromURL(Component) {
  let C = Memoized.get(Component);
  if (!C) {
    C = function NamespaceInjector(props) {
      return <Component namespace={props.match.params.ns} {...props} />;
    };
    Memoized.set(Component, C);
  }
  return C;
}

const namespacedRoutes = [];
_.each(namespacedPrefixes, (p) => {
  namespacedRoutes.push(`${p}/ns/:ns`);
  namespacedRoutes.push(`${p}/all-namespaces`);
});

const DefaultPageRedirect: React.FC<{
  url: Perspective['properties']['landingPageURL'];
  flags: { [key: string]: boolean };
  firstVisit: boolean;
}> = ({ url, flags, firstVisit }) => {
  const [resolvedUrl, setResolvedUrl] = React.useState<string>();
  React.useEffect(() => {
    (async () => {
      setResolvedUrl((await url())(flags, firstVisit));
    })();
  }, [url, flags, firstVisit]);

  return resolvedUrl ? <Redirect to={resolvedUrl} /> : null;
};

type DefaultPageProps = {
  flags: FlagsObject;
};

// The default page component lets us connect to flags without connecting the entire App.
const DefaultPage_: React.FC<DefaultPageProps> = ({ flags }) => {
  const [activePerspective] = useActivePerspective();
  const perspectiveExtensions = useExtensions<Perspective>(isPerspective);
  const [visited, setVisited, visitedLoaded] = useUserSettings<boolean>(
    getPerspectiveVisitedKey(activePerspective),
    false,
  );
  const firstVisit = React.useRef<boolean>();

  // First time thru, capture first visit status
  if (firstVisit.current == null && visitedLoaded) {
    firstVisit.current = !visited;
  }

  React.useEffect(() => {
    if (visitedLoaded && !visited) {
      // Mark perspective as visited
      setVisited(true);
    }
  }, [visitedLoaded, visited, setVisited]);

  if (Object.keys(flags).some((key) => flagPending(flags[key])) || !visitedLoaded) {
    return <LoadingBox />;
  }

  const perspective = perspectiveExtensions.find((p) => p.properties.id === activePerspective);

  // support redirecting to perspective landing page
  return (
    <DefaultPageRedirect
      flags={flags}
      firstVisit={firstVisit.current}
      url={perspective.properties.landingPageURL}
    />
  );
};

const DefaultPage = connectToFlags(FLAGS.OPENSHIFT, FLAGS.CAN_LIST_NS)(DefaultPage_);

const LazyRoute = (props) => (
  <Route
    {...props}
    component={undefined}
    render={(componentProps) => (
      <AsyncComponent loader={props.loader} kind={props.kind} {...componentProps} />
    )}
  />
);

const LazyDynamicRoute: React.FC<Omit<React.ComponentProps<typeof Route>, 'component'> & {
  component: LoadedExtension<DynamicRoutePage>['properties']['component'];
}> = ({ component, ...props }) => {
  const LazyComponent = React.useMemo(
    () =>
      React.lazy(async () => {
        const Component = await component();
        // TODO do not wrap as `default` when we support module code refs
        return { default: Component };
      }),
    [component],
  );
  return <Route {...props} component={LazyComponent} />;
};

const getPluginPageRoutes = (
  activePerspective: string,
  setActivePerspective: (perspective: string) => void,
  routePages: RoutePage[],
  dynamicRoutePages: LoadedExtension<DynamicRoutePage>[],
) => {
  const activeRoutes = [
    ...routePages
      .filter((r) => !r.properties.perspective || r.properties.perspective === activePerspective)
      .map((r) => {
        const Component = r.properties.loader ? LazyRoute : Route;
        return <Component {...r.properties} key={Array.from(r.properties.path).join(',')} />;
      }),
    ...dynamicRoutePages
      .filter((r) => !r.properties.perspective || r.properties.perspective === activePerspective)
      .map((r) => (
        <LazyDynamicRoute
          exact={r.properties.exact}
          path={r.properties.path}
          component={r.properties.component}
          key={r.uid}
        />
      )),
  ];

  const inactiveRoutes = [...routePages, ...dynamicRoutePages]
    .filter((r) => r.properties.perspective && r.properties.perspective !== activePerspective)
    .map((r) => {
      const key = Array.from(r.properties.path)
        .concat([r.properties.perspective])
        .join(',');

      return (
        <Route
          {...r.properties}
          key={key}
          component={() => {
            React.useEffect(() => setActivePerspective(r.properties.perspective));
            return null;
          }}
        />
      );
    });

  return [activeRoutes, inactiveRoutes];
};

const AppContents: React.FC<{}> = () => {
  const [activePerspective, setActivePerspective] = useActivePerspective();
  const routePageExtensions = useExtensions<RoutePage>(isRoutePage);
  const dynamicRoutePages = useExtensions<DynamicRoutePage>(isDynamicRoutePage);
  const [, allPluginsProcessed] = useDynamicPluginInfo();

  const [pluginPageRoutes, inactivePluginPageRoutes] = React.useMemo(
    () =>
      getPluginPageRoutes(
        activePerspective,
        setActivePerspective,
        routePageExtensions,
        dynamicRoutePages,
      ),
    [activePerspective, setActivePerspective, routePageExtensions, dynamicRoutePages],
  );

  return (
    <PageSection variant={PageSectionVariants.light}>
      <div id="content">
        <GlobalNotifications />
        <Route path={namespacedRoutes} component={NamespaceBar} />

        <div id="content-scrollable">
          <React.Suspense fallback={<LoadingBox />}>
            <Switch>
              {pluginPageRoutes}

              <Route path={['/all-namespaces', '/ns/:ns']} component={RedirectComponent} />
              <LazyRoute
                path="/dashboards"
                loader={() =>
                  import(
                    './dashboard/dashboards-page/dashboards' /* webpackChunkName: "dashboards" */
                  ).then((m) => m.DashboardsPage)
                }
              />

              {/* Redirect legacy routes to avoid breaking links */}
              <Redirect from="/cluster-status" to="/dashboards" />
              <Redirect from="/status/all-namespaces" to="/dashboards" />
              <Redirect from="/status/ns/:ns" to="/k8s/cluster/projects/:ns" />
              <Route path="/status" exact component={NamespaceRedirect} />
              <Redirect from="/overview/all-namespaces" to="/dashboards" />
              <Redirect from="/overview/ns/:ns" to="/k8s/cluster/projects/:ns/workloads" />
              <Route path="/overview" exact component={NamespaceRedirect} />

              <LazyRoute
                path="/api-explorer"
                exact
                loader={() =>
                  import('./api-explorer' /* webpackChunkName: "api-explorer" */).then(
                    (m) => m.APIExplorerPage,
                  )
                }
              />
              <LazyRoute
                path="/api-resource/cluster/:plural"
                loader={() =>
                  import('./api-explorer' /* webpackChunkName: "api-explorer" */).then(
                    (m) => m.APIResourcePage,
                  )
                }
              />
              <LazyRoute
                path="/api-resource/all-namespaces/:plural"
                loader={() =>
                  import('./api-explorer' /* webpackChunkName: "api-explorer" */).then((m) =>
                    NamespaceFromURL(m.APIResourcePage),
                  )
                }
              />
              <LazyRoute
                path="/api-resource/ns/:ns/:plural"
                loader={() =>
                  import('./api-explorer' /* webpackChunkName: "api-explorer" */).then((m) =>
                    NamespaceFromURL(m.APIResourcePage),
                  )
                }
              />

              <LazyRoute
                path="/command-line-tools"
                exact
                loader={() =>
                  import('./command-line-tools' /* webpackChunkName: "command-line-tools" */).then(
                    (m) => m.CommandLineToolsPage,
                  )
                }
              />

              <Route path="/operatorhub" exact component={NamespaceRedirect} />
              <LazyRoute
                path="/provisionedservices/all-namespaces"
                loader={() =>
                  import(
                    './provisioned-services' /* webpackChunkName: "provisionedservices" */
                  ).then((m) => m.ProvisionedServicesPage)
                }
              />
              <LazyRoute
                path="/provisionedservices/ns/:ns"
                loader={() =>
                  import(
                    './provisioned-services' /* webpackChunkName: "provisionedservices" */
                  ).then((m) => m.ProvisionedServicesPage)
                }
              />
              <Route path="/provisionedservices" component={NamespaceRedirect} />

              <LazyRoute
                path="/brokermanagement"
                loader={() =>
                  import('./broker-management' /* webpackChunkName: "brokermanagment" */).then(
                    (m) => m.BrokerManagementPage,
                  )
                }
              />

              <LazyRoute
                path="/catalog/instantiate-template"
                exact
                loader={() =>
                  import(
                    './instantiate-template' /* webpackChunkName: "instantiate-template" */
                  ).then((m) => m.InstantiateTemplatePage)
                }
              />

              <Route
                path="/k8s/ns/:ns/alertmanagers/:name"
                exact
                render={({ match }) => (
                  <Redirect
                    to={`/k8s/ns/${match.params.ns}/${referenceForModel(AlertmanagerModel)}/${
                      match.params.name
                    }`}
                  />
                )}
              />

              <LazyRoute
                path="/k8s/all-namespaces/events"
                exact
                loader={() =>
                  import('./events' /* webpackChunkName: "events" */).then((m) =>
                    NamespaceFromURL(m.EventStreamPage),
                  )
                }
              />
              <LazyRoute
                path="/k8s/ns/:ns/events"
                exact
                loader={() =>
                  import('./events' /* webpackChunkName: "events" */).then((m) =>
                    NamespaceFromURL(m.EventStreamPage),
                  )
                }
              />
              <Route path="/search/all-namespaces" exact component={NamespaceFromURL(SearchPage)} />
              <Route path="/search/ns/:ns" exact component={NamespaceFromURL(SearchPage)} />
              <Route path="/search" exact component={NamespaceRedirect} />

              <LazyRoute
                path="/k8s/all-namespaces/import"
                exact
                loader={() =>
                  import('./import-yaml' /* webpackChunkName: "import-yaml" */).then((m) =>
                    NamespaceFromURL(m.ImportYamlPage),
                  )
                }
              />
              <LazyRoute
                path="/k8s/ns/:ns/import/"
                exact
                loader={() =>
                  import('./import-yaml' /* webpackChunkName: "import-yaml" */).then((m) =>
                    NamespaceFromURL(m.ImportYamlPage),
                  )
                }
              />

              {
                // These pages are temporarily disabled. We need to update the safe resources list.
                // <LazyRoute path="/k8s/cluster/clusterroles/:name/add-rule" exact loader={() => import('./RBAC' /* webpackChunkName: "rbac" */).then(m => m.EditRulePage)} />
                // <LazyRoute path="/k8s/cluster/clusterroles/:name/:rule/edit" exact loader={() => import('./RBAC' /* webpackChunkName: "rbac" */).then(m => m.EditRulePage)} />
              }

              {
                // <LazyRoute path="/k8s/ns/:ns/roles/:name/add-rule" exact loader={() => import('./RBAC' /* webpackChunkName: "rbac" */).then(m => m.EditRulePage)} />
                // <LazyRoute path="/k8s/ns/:ns/roles/:name/:rule/edit" exact loader={() => import('./RBAC' /* webpackChunkName: "rbac" */).then(m => m.EditRulePage)} />
              }

              <LazyRoute
                path="/k8s/ns/:ns/secrets/~new/:type"
                exact
                kind="Secret"
                loader={() =>
                  import('./secrets/create-secret' /* webpackChunkName: "create-secret" */).then(
                    (m) => m.CreateSecret,
                  )
                }
              />
              <LazyRoute
                path="/k8s/ns/:ns/secrets/:name/edit"
                exact
                kind="Secret"
                loader={() =>
                  import('./secrets/create-secret' /* webpackChunkName: "create-secret" */).then(
                    (m) => m.EditSecret,
                  )
                }
              />
              <LazyRoute
                path="/k8s/ns/:ns/secrets/:name/edit-yaml"
                exact
                kind="Secret"
                loader={() => import('./create-yaml').then((m) => m.EditYAMLPage)}
              />

              <LazyRoute
                path="/k8s/ns/:ns/networkpolicies/~new/form"
                exact
                kind="NetworkPolicy"
                loader={() =>
                  import(
                    '@console/app/src/components/network-policies/create-network-policy' /* webpackChunkName: "create-network-policy" */
                  ).then((m) => m.CreateNetworkPolicy)
                }
              />

              <LazyRoute
                path="/k8s/ns/:ns/routes/~new/form"
                exact
                kind="Route"
                loader={() =>
                  import('./routes/create-route' /* webpackChunkName: "create-route" */).then(
                    (m) => m.CreateRoute,
                  )
                }
              />

              <LazyRoute
                path="/k8s/cluster/rolebindings/~new"
                exact
                loader={() =>
                  import('./RBAC' /* webpackChunkName: "rbac" */).then((m) => m.CreateRoleBinding)
                }
                kind="RoleBinding"
              />
              <LazyRoute
                path="/k8s/ns/:ns/rolebindings/~new"
                exact
                loader={() =>
                  import('./RBAC' /* webpackChunkName: "rbac" */).then((m) => m.CreateRoleBinding)
                }
                kind="RoleBinding"
              />
              <LazyRoute
                path="/k8s/ns/:ns/rolebindings/:name/copy"
                exact
                kind="RoleBinding"
                loader={() =>
                  import('./RBAC' /* webpackChunkName: "rbac" */).then((m) => m.CopyRoleBinding)
                }
              />
              <LazyRoute
                path="/k8s/ns/:ns/rolebindings/:name/edit"
                exact
                kind="RoleBinding"
                loader={() =>
                  import('./RBAC' /* webpackChunkName: "rbac" */).then((m) => m.EditRoleBinding)
                }
              />
              <LazyRoute
                path="/k8s/cluster/clusterrolebindings/:name/copy"
                exact
                kind="ClusterRoleBinding"
                loader={() =>
                  import('./RBAC' /* webpackChunkName: "rbac" */).then((m) => m.CopyRoleBinding)
                }
              />
              <LazyRoute
                path="/k8s/cluster/clusterrolebindings/:name/edit"
                exact
                kind="ClusterRoleBinding"
                loader={() =>
                  import('./RBAC' /* webpackChunkName: "rbac" */).then((m) => m.EditRoleBinding)
                }
              />
              <LazyRoute
                path="/k8s/ns/:ns/:plural/:name/attach-storage"
                exact
                loader={() =>
                  import('./storage/attach-storage' /* webpackChunkName: "attach-storage" */).then(
                    (m) => m.default,
                  )
                }
              />

              <LazyRoute
                path="/k8s/ns/:ns/persistentvolumeclaims/~new/form"
                exact
                kind="PersistentVolumeClaim"
                loader={() =>
                  import('./storage/create-pvc' /* webpackChunkName: "create-pvc" */).then(
                    (m) => m.CreatePVC,
                  )
                }
              />

              <LazyRoute
                path={`/k8s/ns/:ns/${VolumeSnapshotModel.plural}/~new/form`}
                exact
                loader={() =>
                  import(
                    '@console/app/src/components/volume-snapshot/create-volume-snapshot/create-volume-snapshot' /* webpackChunkName: "create-volume-snapshot" */
                  ).then((m) => m.VolumeSnapshot)
                }
              />

              <LazyRoute
                path={`/k8s/all-namespaces/${VolumeSnapshotModel.plural}/~new/form`}
                exact
                loader={() =>
                  import(
                    '@console/app/src/components/volume-snapshot/create-volume-snapshot/create-volume-snapshot' /* webpackChunkName: "create-volume-snapshot" */
                  ).then((m) => m.VolumeSnapshot)
                }
              />

              <LazyRoute
                path="/monitoring/alerts"
                exact
                loader={() =>
                  import('./monitoring/alerting' /* webpackChunkName: "alerting" */).then(
                    (m) => m.MonitoringUI,
                  )
                }
              />
              <LazyRoute
                path="/monitoring/alertrules"
                exact
                loader={() =>
                  import('./monitoring/alerting' /* webpackChunkName: "alerting" */).then(
                    (m) => m.MonitoringUI,
                  )
                }
              />
              <LazyRoute
                path="/monitoring/silences"
                exact
                loader={() =>
                  import('./monitoring/alerting' /* webpackChunkName: "alerting" */).then(
                    (m) => m.MonitoringUI,
                  )
                }
              />
              <LazyRoute
                path="/monitoring/alertmanageryaml"
                exact
                loader={() =>
                  import('./monitoring/alerting' /* webpackChunkName: "alerting" */).then(
                    (m) => m.MonitoringUI,
                  )
                }
              />
              <LazyRoute
                path="/monitoring/alertmanagerconfig"
                exact
                loader={() =>
                  import('./monitoring/alerting' /* webpackChunkName: "alerting" */).then(
                    (m) => m.MonitoringUI,
                  )
                }
              />
              <LazyRoute
                path="/monitoring/alertmanagerconfig/receivers/~new"
                exact
                loader={() =>
                  import(
                    './monitoring/receiver-forms/alert-manager-receiver-forms' /* webpackChunkName: "receiver-forms" */
                  ).then((m) => m.CreateReceiver)
                }
              />
              <LazyRoute
                path="/monitoring/alertmanagerconfig/receivers/:name/edit"
                exact
                loader={() =>
                  import(
                    './monitoring/receiver-forms/alert-manager-receiver-forms' /* webpackChunkName: "receiver-forms" */
                  ).then((m) => m.EditReceiver)
                }
              />
              <LazyRoute
                path="/monitoring"
                loader={() =>
                  import('./monitoring/alerting' /* webpackChunkName: "alerting" */).then(
                    (m) => m.MonitoringUI,
                  )
                }
              />

              <LazyRoute
                path="/settings/idp/github"
                exact
                loader={() =>
                  import(
                    './cluster-settings/github-idp-form' /* webpackChunkName: "github-idp-form" */
                  ).then((m) => m.AddGitHubPage)
                }
              />
              <LazyRoute
                path="/settings/idp/gitlab"
                exact
                loader={() =>
                  import(
                    './cluster-settings/gitlab-idp-form' /* webpackChunkName: "gitlab-idp-form" */
                  ).then((m) => m.AddGitLabPage)
                }
              />
              <LazyRoute
                path="/settings/idp/google"
                exact
                loader={() =>
                  import(
                    './cluster-settings/google-idp-form' /* webpackChunkName: "google-idp-form" */
                  ).then((m) => m.AddGooglePage)
                }
              />
              <LazyRoute
                path="/settings/idp/htpasswd"
                exact
                loader={() =>
                  import(
                    './cluster-settings/htpasswd-idp-form' /* webpackChunkName: "htpasswd-idp-form" */
                  ).then((m) => m.AddHTPasswdPage)
                }
              />
              <LazyRoute
                path="/settings/idp/keystone"
                exact
                loader={() =>
                  import(
                    './cluster-settings/keystone-idp-form' /* webpackChunkName: "keystone-idp-form" */
                  ).then((m) => m.AddKeystonePage)
                }
              />
              <LazyRoute
                path="/settings/idp/ldap"
                exact
                loader={() =>
                  import(
                    './cluster-settings/ldap-idp-form' /* webpackChunkName: "ldap-idp-form" */
                  ).then((m) => m.AddLDAPPage)
                }
              />
              <LazyRoute
                path="/settings/idp/oidconnect"
                exact
                loader={() =>
                  import(
                    './cluster-settings/openid-idp-form' /* webpackChunkName: "openid-idp-form" */
                  ).then((m) => m.AddOpenIDIDPPage)
                }
              />
              <LazyRoute
                path="/settings/idp/basicauth"
                exact
                loader={() =>
                  import(
                    './cluster-settings/basicauth-idp-form' /* webpackChunkName: "basicauth-idp-form" */
                  ).then((m) => m.AddBasicAuthPage)
                }
              />
              <LazyRoute
                path="/settings/idp/requestheader"
                exact
                loader={() =>
                  import(
                    './cluster-settings/request-header-idp-form' /* webpackChunkName: "request-header-idp-form" */
                  ).then((m) => m.AddRequestHeaderPage)
                }
              />
              <LazyRoute
                path="/settings/cluster"
                loader={() =>
                  import(
                    './cluster-settings/cluster-settings' /* webpackChunkName: "cluster-settings" */
                  ).then((m) => m.ClusterSettingsPage)
                }
              />

              <LazyRoute
                path={'/k8s/cluster/storageclasses/~new/form'}
                exact
                loader={() =>
                  import('./storage-class-form' /* webpackChunkName: "storage-class-form" */).then(
                    (m) => m.StorageClassForm,
                  )
                }
              />

              <Route path="/k8s/cluster/:plural" exact component={ResourceListPage} />
              <Route path="/k8s/cluster/:plural/~new" exact component={CreateResource} />
              <Route path="/k8s/cluster/:plural/:name" component={ResourceDetailsPage} />
              <LazyRoute
                path="/k8s/ns/:ns/pods/:podName/containers/:name"
                loader={() => import('./container').then((m) => m.ContainersDetailsPage)}
              />
              <Route path="/k8s/ns/:ns/:plural/~new" exact component={CreateResource} />
              <Route path="/k8s/ns/:ns/:plural/:name" component={ResourceDetailsPage} />
              <Route path="/k8s/ns/:ns/:plural" exact component={ResourceListPage} />

              <Route path="/k8s/all-namespaces/:plural" exact component={ResourceListPage} />
              <Route path="/k8s/all-namespaces/:plural/:name" component={ResourceDetailsPage} />

              {inactivePluginPageRoutes}

              <LazyRoute
                path="/error"
                exact
                loader={() =>
                  import('./error' /* webpackChunkName: "error" */).then((m) => m.ErrorPage)
                }
              />
              <Route path="/" exact component={DefaultPage} />

              {allPluginsProcessed ? (
                <LazyRoute
                  loader={() =>
                    import('./error' /* webpackChunkName: "error" */).then((m) => m.ErrorPage404)
                  }
                />
              ) : (
                <Route component={LoadingBox} />
              )}
            </Switch>
          </React.Suspense>
        </div>
      </div>
    </PageSection>
  );
};

export default AppContents;
