import React, { Suspense, useEffect } from "react";
import { Helmet, HelmetProvider } from "react-helmet-async";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { KeycloakProvider } from "@react-keycloak/web";
import { ExtraErrorData } from "@sentry/integrations/dist/extraerrordata";
import * as Sentry from "@sentry/react";
import { Integrations } from "@sentry/tracing";

import { loadAnnouncements } from "actions/announcements";
import { loadConfig } from "actions/global-info";
import { loadPosts } from "actions/posts";
import { loadProgram } from "actions/program";
import { loadMe } from "actions/users";
import { initializeWSChannel } from "actions/ws";
import Footer from "components/Footer";
import Navbar from "components/Navbar";
import About from "pages/About";
import Home from "pages/Home";
import NotFound from "pages/NotFound";
import Program from "pages/Program";
import Protocol from "pages/Protocol";
import { AuthStore, PostStore } from "stores";
import { updateWindowPosts } from "utils";

import keycloak from "./keycloak";

/**
 * If configured, set up Sentry client that reports uncaught errors down to
 * https://sentry.io.
 */
if (process.env.REACT_APP_SENTRY_DSN) {
  Sentry.init({
    dsn: process.env.REACT_APP_SENTRY_DSN,
    tracesSampleRate: 0.1,
    integrations: [new ExtraErrorData(), new Integrations.BrowserTracing()],
  });
}

const onKeycloakEvent = (event) => {
  if (["onAuthRefreshSuccess", "onAuthSuccess"].includes(event)) {
    Sentry.setUser(keycloak.tokenParsed);

    const kcRoles = keycloak.tokenParsed.roles;
    let role = null;

    if (kcRoles.includes("chairman")) {
      role = "chairman";
    } else if (kcRoles.includes("member")) {
      role = "member";
    } else {
      role = "regp";
    }

    AuthStore.update((state) => {
      state.isAuthenticated = true;
      state.user = {
        name: keycloak.tokenParsed.name,
        username: keycloak.tokenParsed.preferred_username,
        role,
        accessToken: keycloak.token,
      };
    });

    // Once base user details has been stored, load me details from API.
    loadMe.run();

    PostStore.update((state) => {
      // Only display proposals verified by chairman to other users.
      state.filters.showPendingProposals = role === "chairman";
      updateWindowPosts(state);
    });
  }
};

const LoadingComponent = (
  <div className="h-screen w-screen flex justify-center items-center">
    <div className="text-center">
      <div className="flex flex-col md:flex-row items-center space-x-4 text-center mb-2">
        <img
          src={`${process.env.REACT_APP_STYLEGUIDE_URL}/images/logo-round-black.svg`}
          className="w-16 mb-2"
          alt="Pirátská strana"
        />
        <h1 className="head-alt-md md:head-alt-lg">Celostátní fórum 2021</h1>
      </div>
      <p className="text-center head-xs md:head-base">Načítám aplikaci ...</p>
    </div>
  </div>
);

const BaseApp = () => {
  useEffect(() => {
    initializeWSChannel.run();
  }, []);

  return (
    <HelmetProvider>
      <Router>
        <Helmet>
          <title>CF 2021 | Pirátská strana</title>
          <meta
            name="description"
            content="Oficiální stránka letošního ročníku on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
          />
          <meta property="og:title" content="CF 2021 | Pirátská strana" />
          <meta
            property="og:description"
            content="Oficiální stránka letošního ročníku on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
          />
        </Helmet>
        <Navbar />
        <Switch>
          <Route exact path="/" children={<Home />} />
          <Route exact path="/program" children={<Program />} />
          <Route exact path="/protocol" children={<Protocol />} />
          <Route exact path="/about" children={<About />} />
          <Route component={NotFound} />
        </Switch>
        <Footer />
      </Router>
    </HelmetProvider>
  );
};

const ConfiguredApp = () => {
  loadConfig.read();
  loadProgram.read();
  loadAnnouncements.read();
  loadPosts.read();

  return (
    <Suspense fallback={LoadingComponent}>
      <BaseApp />
    </Suspense>
  );
};

const AuthenticatedApp = () => {
  const keycloakInitConfig = {
    onLoad: "check-sso",
    // Necessary to prevent Keycloak cookie issues:
    // @see: https://stackoverflow.com/a/63588334/303184
    checkLoginIframe: false,
  };

  return (
    <>
      <KeycloakProvider
        keycloak={keycloak}
        initConfig={keycloakInitConfig}
        LoadingComponent={LoadingComponent}
        onEvent={onKeycloakEvent}
      >
        <Suspense fallback={LoadingComponent}>
          <ConfiguredApp />
        </Suspense>
      </KeycloakProvider>
    </>
  );
};

const ErrorBoundaryFallback = ({ error }) => {
  return (
    <div className="h-screen w-screen flex justify-center items-center">
      <div className="text-center">
        <h1 className="head-alt-xl text-red-600 mb-4">
          V aplikaci došlo k chybě :(
        </h1>
        <p className="text-lg leading-normal">
          Naši vývojáři o tom již byli informování a opraví to co nejdříve.
          <br />
          Omlouváme se za tuto nepříjemnost.
        </p>
        <a href="/" className="btn mt-8">
          <div className="btn__body">Načíst znovu</div>
        </a>
      </div>
    </div>
  );
};

const App = Sentry.withProfiler(() => {
  return (
    <Sentry.ErrorBoundary fallback={ErrorBoundaryFallback} showDialog>
      <AuthenticatedApp />
    </Sentry.ErrorBoundary>
  );
});

export default App;