import {
  ChakraProvider,
  Container,
  cookieStorageManagerSSR,
  Flex,
  Heading,
  Link,
  Spacer,
  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,
  setUser,
  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, hide: true }}>
          <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>
      );

    case 503:
      return (
        <DocumentWrapper loaderData={{ hide: true }}>
          <Container
            left="50%"
            maxW="container.md"
            pos="absolute"
            top="50%"
            transform="translate(-50%, -50%)"
          >
            <Flex>
              <Spacer />
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="128"
                height="128"
                fill="currentColor"
                viewBox="0 0 16 16"
              >
                <path d="M12.496 8a4.5 4.5 0 0 1-1.703 3.526L9.497 8.5l2.959-1.11q.04.3.04.61" />
                <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-1 0a7 7 0 1 0-13.202 3.249l1.988-1.657a4.5 4.5 0 0 1 7.537-4.623L7.497 6.5l1 2.5 1.333 3.11c-.56.251-1.18.39-1.833.39a4.5 4.5 0 0 1-1.592-.29L4.747 14.2A7 7 0 0 0 15 8m-8.295.139a.25.25 0 0 0-.288-.376l-1.5.5.159.474.808-.27-.595.894a.25.25 0 0 0 .287.376l.808-.27-.595.894a.25.25 0 0 0 .287.376l1.5-.5-.159-.474-.808.27.596-.894a.25.25 0 0 0-.288-.376l-.808.27z" />
              </svg>
              <Spacer />
            </Flex>
            <br />
            <Heading textAlign="center">
              The engineers are breaking stuff again
            </Heading>
            <br />
            <Text textAlign="center">
              Someday they will finish, come back later.
            </Text>
          </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 }> {
  if (context.data.mx)
    throw new Response(null, {
      status: 503,
    });

  let data: { [k: string]: string } = {};

  if (context.env.COMMIT_SHA) data.commit_sha = context.env.COMMIT_SHA;
  if (context.data.current_user) data = { ...context.data.current_user };
  if (context.env.REMIX_DSN) data.dsn = context.env.REMIX_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.commit_sha ? (
              <meta name="commit_sha" content={loaderData.commit_sha} />
            ) : null}
            {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>();

  if (
    loaderData.id &&
    [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].find(
      (p) => loaderData.permissions & (1 << p),
    )
  )
    setUser({
      email: loaderData.email,
      id: loaderData.id,
      username: loaderData.username,
    });

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

export default withSentry(App);
