import { ApolloProvider } from "@apollo/react-hooks";
import styled from "@emotion/styled";
import { InMemoryCache } from "apollo-cache-inmemory";
import { ApolloClient } from "apollo-client";
import { ApolloLink } from "apollo-link";
import { setContext } from "apollo-link-context";
import { onError } from "apollo-link-error";
import { createUploadLink } from "apollo-upload-client";
import React, { useEffect, useState } from "react";
import { MemoryRouter, Route, Switch } from "react-router-dom";
import LogIn from "./components/LogIn";
import AuthRoute from "./components/routes/AuthRoute";
import Cheatsheet from "./components/shared/platform/Cheatsheet";
import CommandLine from "./components/shared/platform/CommandLine";
import Notification from "./components/shared/platform/Notification";
import SignUp from "./components/SignUp";
import SlideshowPage from "./components/SlideshowPage";
import PlatformContext from "./platform/PlatformContext";
import ShortcutManager from "./platform/ShortcutManager";
import MainPage from "./presentable/MainPage";
import CommandLineProvider from "./providers/CommandLineProvider";
import ModalProvider from "./providers/ModalProvider";
import { getItem } from "./storage";
import { getBackendGraphqlUrl } from "./utils/urls";

const authLink = setContext(async (_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = await getItem("authToken");
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    },
  };
});

const uploadLink = createUploadLink({
  uri: getBackendGraphqlUrl(),
  credentials: "same-origin",
});

export const client = new ApolloClient({
  link: ApolloLink.from([
    onError(({ graphQLErrors, networkError }) => {
      if (graphQLErrors)
        graphQLErrors.forEach(({ message, locations, path }) =>
          // don't remove this log
          console.error(
            `[GraphQL error]: Message: ${JSON.stringify(
              message,
              null,
              2
            )}, Location: ${JSON.stringify(
              locations,
              null,
              2
            )}, Path: ${JSON.stringify(path, null, 2)}`
          )
        );
      // don't remove this log
      if (networkError)
        console.error(
          `[Network error]: ${JSON.stringify(networkError, null, 2)}`
        );
    }),
    authLink,
    uploadLink,
  ]),
  cache: new InMemoryCache({}),
});

const shortcutManager = new ShortcutManager();

const App: React.FC = () => {
  const [notification, setNotification] = useState<string | null>(null);
  const [timestamp, setTimestamp] = useState<number | null>(null);
  const [showCheatsheet, setShowCheatsheet] = useState<boolean>(false);
  const [showSidebar, setShowSidebar] = useState<boolean>(false);

  useEffect(() => {
    shortcutManager.bind(
      "?",
      () => {
        if (showCheatsheet) {
          setShowCheatsheet(false);
        } else {
          setShowCheatsheet(true);
        }
      },
      "App",
      "Show / hide Shortcuts",
      1
    );
    return () => {
      shortcutManager.unbind("?", "App");
    };
  });

  const setNotificationAndTimestamp = (notificationAndTimestamp: {
    notification: string;
    timestamp: number;
  }) => {
    setNotification(notificationAndTimestamp.notification);
    setTimestamp(notificationAndTimestamp.timestamp);
  };

  return (
    <AppContainer>
      <PlatformContext.Provider
        value={{
          manager: shortcutManager,
          notification: notification,
          notificationTimestamp: timestamp,
          setNotification: setNotificationAndTimestamp,
          showSidebar: showSidebar,
          setShowSidebar: setShowSidebar,
        }}
      >
        <ApolloProvider client={client}>
          <MemoryRouter>
            <ModalProvider>
              <CommandLineProvider>
                <Routes />
                {showCheatsheet && (
                  <Cheatsheet hideCheatsheet={() => setShowCheatsheet(false)} />
                )}
                <CommandLine />
              </CommandLineProvider>
            </ModalProvider>
          </MemoryRouter>
          <Notification />
        </ApolloProvider>
      </PlatformContext.Provider>
    </AppContainer>
  );
};

const Routes: React.FC = () => {
  return (
    <Switch>
      <AuthRoute exact path="/" component={MainPage as any} />
      <AuthRoute path="/slideshow/:id" component={SlideshowPage as any} />
      <Route exact path="/signup" component={SignUp} />
      <Route exact path="/login" component={LogIn} />
    </Switch>
  );
};

export default App;

const AppContainer = styled.div`
  display: flex;
  max-height: 100vh;
  max-width: 100vw;
  width: 100%;
  overflow-x: hidden;
  justify-content: center;
  color: black;
`;
