import {
  ChakraProvider,
  Container,
  cookieStorageManagerSSR,
  Heading,
  Link,
  Text,
} from "@chakra-ui/react";
import { ClientStyleContext, ServerStyleContext } from "./context.js";
import fontStyle from "@fontsource-variable/plus-jakarta-sans/index.css";
import Forbidden from "../components/Forbidden.js";
import globalStyles from "../index.css";
import {
  isRouteErrorResponse,
  Links,
  Meta,
  Outlet,
  Scripts,
  useLoaderData,
  useRouteError,
  useRouteLoaderData,
} from "@remix-run/react";
import { type ErrorResponse } from "@remix-run/router";
import { LinksFunction } from "@remix-run/cloudflare";
import Login from "../components/Login.js";
import Navigation from "../components/Navigation.js";
import { type ReactNode, StrictMode, useContext, useEffect } from "react";
import theme from "../theme.js";
import { withEmotionCache } from "@emotion/react";
import { captureRemixErrorBoundaryError, withSentry } from "@sentry/remix";

export function ErrorBoundary() {
  const error = useRouteError() as ErrorResponse;

  if (!isRouteErrorResponse(error))
    return (
      <DocumentWrapper loaderData={{ hide: true }}>
        <Container maxW="container.lg" pt="8vh" textAlign="left">
          <Heading size="4xl">???</Heading>
          <br />
          <Text fontSize="xl">Something bad happened!</Text>
          <br />
          <br />
          <br />
          <Text>Details: {error}</Text>
          <br />
          <br />
          <Link color="#646cff" onClick={() => location.reload()}>
            Refresh
          </Link>
        </Container>
      </DocumentWrapper>
    );

  const { status } = error;
  const loaderData = useRouteLoaderData<typeof loader>("root") || {};

  switch (status) {
    case 303:
      return "";

    case 401:
      return (
        <DocumentWrapper loaderData={loaderData}>
          <Login />
        </DocumentWrapper>
      );

    case 403:
      return (
        <DocumentWrapper loaderData={loaderData}>
          <Forbidden />
        </DocumentWrapper>
      );

    case 404:
      return (
        <DocumentWrapper loaderData={loaderData}>
          <Container maxW="container.lg" pt="8vh" textAlign="left">
            <Heading size="4xl">404</Heading>
            <br />
            <Text fontSize="xl">There is nothing to find here.</Text>
            <br />
            <br />
            <br />
            <Link color="#646cff" onClick={() => history.go(-1)}>
              Go back
            </Link>
          </Container>
        </DocumentWrapper>
      );

    default:
      captureRemixErrorBoundaryError(useRouteError());
      return (
        <DocumentWrapper loaderData={loaderData}>
          <Container maxW="container.lg" pt="8vh" textAlign="left">
            <Heading size="4xl">500</Heading>
            <br />
            <Text fontSize="xl">S̶̡͈̠̗̠͖͙̭o̶̶͕͚̥͍̪̤m̸̨͏͈͔̖͚̖̰̱͞e҉̵͖͚͇̀t̕͟͠͏͎̺̯̲̱̣̤̠̟͙̠̙̫̬ḩ̸̭͓̬͎̙̀į̞̮͉͖̰̥̹͚̫̙̪̗̜̳̕ͅn҉͔̯̪̗̝̝͖̲͇͍͎̲̲̤̖̫͈̪͡g̴̰̻̙̝͉̭͇̖̰̝̙͕̼͙͘͜ ̵̶̫̥̳̲̘̻̗͈͕̭̲͇̘̜̺̟̥̖̥b̴̙̭̹͕̞͠r̞͎̠̩͈̖̰̞̯̯͢͢͠ͅo̝̯̗̹̳͍̰͉͕̘̰̠̺̥̰͔̕ͅk̵̸̻̠͕̺̦̦͖̲̺̦̞̝̞͞͡e̶͏̤̼̼͔̘̰̰̭͈̀͞͡</Text>
            <br />
            <br />
            <br />
            <Link color="#646cff" onClick={() => location.reload()}>
              Reload
            </Link>
          </Container>
        </DocumentWrapper>
      );
  }
}

export const links: LinksFunction = () => {
  return [
    { href: "/favicon.ico", rel: "icon" },
    { href: "/files/logo192.png", rel: "apple-touch-icon", type: "image/png" },
    { href: fontStyle, rel: "stylesheet" },
    { href: globalStyles, rel: "stylesheet" },
  ];
};

export async function loader({
  context,
}: {
  context: RequestContext;
}): Promise<{ [k: string]: any }> {
  let data: { [k: string]: string } = {};

  if (context.data.current_user) data = { ...context.data.current_user };
  if (context.env.DSN) data.dsn = context.env.DSN;
  if (context.data.nonce) data.nonce = context.data.nonce;
  if (context.data.theme) data.theme = context.data.theme;

  return data;
}

export function meta() {
  return [{ title: "Car Crushers" }];
}

function DocumentWrapper(props: {
  loaderData: { [k: string]: any };
  children: ReactNode;
}) {
  const { children: child, loaderData } = props;
  const Document = withEmotionCache(
    ({ children }: { children: ReactNode }, emotionCache) => {
      const serverStyleData = useContext(ServerStyleContext);
      const clientStyleData = useContext(ClientStyleContext);

      useEffect(() => {
        emotionCache.sheet.container = document.head;
        const tags = emotionCache.sheet.tags;

        emotionCache.sheet.flush();
        tags.forEach((tag) => {
          (emotionCache.sheet as any)._insertTag(tag);
        });

        clientStyleData?.reset();
      }, []);

      const body = (
        <StrictMode>
          <ChakraProvider
            colorModeManager={cookieStorageManagerSSR(
              typeof document === "undefined"
                ? `chakra-ui-color-mode=${loaderData.theme}`
                : document.cookie,
            )}
            theme={theme}
          >
            <div className="App">
              <Navigation {...loaderData} />
              {children}
              <Scripts />
            </div>
          </ChakraProvider>
        </StrictMode>
      );

      return (
        <html data-theme={loaderData.theme} lang="en-US">
          <head>
            <Links />
            <style>
              {`
                :root {
                  color-scheme: ${loaderData.theme};
                }
              `}
            </style>
            {serverStyleData?.map(({ key, ids, css }) => (
              <style
                key={key}
                data-emotion={`${key} ${ids.join(" ")}`}
                dangerouslySetInnerHTML={{ __html: css }}
              />
            ))}
            <meta charSet="UTF-8" />
            {loaderData.dsn ? (
              <meta name="dsn" content={loaderData.dsn} />
            ) : null}
            <meta name="theme-color" content="#00a8f8" />
            <meta
              name="viewport"
              content="width=device-width, initial-scale=1.0"
            />
            <Meta />
          </head>
          <body
            {...(loaderData.theme && {
              className: `chakra-ui-${loaderData.theme}`,
            })}
          >
            {body}
          </body>
        </html>
      );
    },
  );

  return <Document>{child}</Document>;
}

function App() {
  const loaderData = useLoaderData<typeof loader>();

  return (
    <DocumentWrapper loaderData={loaderData}>
      <Outlet />
    </DocumentWrapper>
  );
}

export default withSentry(App);
