import { SWRConfig } from "swr";
import Script from "next/script";
import NProgress from "nprogress";
import type { NextPage } from "next";
import { useRouter } from "next/router";
import type { AppProps } from "next/app";
import App, { AppContext } from "next/app";
import { ReactElement, ReactNode, useEffect, useMemo } from "react";

import { get } from "libs/http";
import { pageview } from "libs/gtag";
import { Context } from "libs/context";
import { GA_TRACKING_ID } from "libs/config";
import Layout from "components/common/Layout";
import { SeoHead, SeoHeadConfig } from "components/SeoHead";
import getSiteConfig, { SiteConfig } from "libs/getSiteConfig";

import "nprogress/nprogress.css";
import "styles/globals.scss";

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
  pageProps: {
    fallback?: Docs;
    siteConfig: SiteConfig;
  };
};

const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => {
  const { fallback, genre, siteConfig, ...otherProps } = pageProps;
  const router = useRouter();

  const seoConfig: SeoHeadConfig = useMemo(() => {
    return {
      title: siteConfig.title,
      description: siteConfig.description,
      keywords: siteConfig.keywords,
      url: `https://${siteConfig.host}`,
      canonical: `https://${siteConfig.host}`,
      jsonLd: JSON.stringify(siteConfig.jsonLd),
      siteName: siteConfig.siteName,
      img: siteConfig.touchIcon,
    };
  }, [
    siteConfig.description,
    siteConfig.host,
    siteConfig.jsonLd,
    siteConfig.keywords,
    siteConfig.siteName,
    siteConfig.title,
    siteConfig.touchIcon,
  ]);

  useEffect(() => {
    const handleStart = () => {
      NProgress.start();
    };

    const handleStop = () => {
      NProgress.done();
    };

    const handleError = (url: string) => {
      pageview(url);
      handleStop();
    };

    router.events.on("routeChangeStart", handleStart);
    router.events.on("routeChangeComplete", handleStop);
    router.events.on("routeChangeError", handleError);

    return () => {
      router.events.off("routeChangeStart", handleStart);
      router.events.off("routeChangeComplete", handleStop);
      router.events.off("routeChangeError", handleError);
    };
  }, [router]);

  return (
    <>
      <SeoHead seoConfig={seoConfig}>
        <meta charSet="utf-8" />
        <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
        <meta
          name="viewport"
          content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
        />
        <link rel="manifest" href="/manifest.json" />
        <link rel="icon" href="/logo.svg" type="image/svg+xml" />
        <link rel="icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="mask-icon" href="/logo.svg" color="#000000" />
        <link
          href="/favicon-16x16.png"
          rel="icon"
          type="image/png"
          sizes="16x16"
        />
        <link
          href="/favicon-32x32.png"
          rel="icon"
          type="image/png"
          sizes="32x32"
        />
        <link rel="apple-touch-icon" href={siteConfig.touchIcon} />
        <meta name="apple-mobile-web-app-title" content={siteConfig.siteName} />
        <meta name="application-name" content={siteConfig.siteName} />
        <meta
          name="theme-color"
          content="#ffffff"
          media="(prefers-color-scheme: light)"
          data-react-helmet="true"
        />
        <meta
          name="theme-color"
          content="#111827"
          media="(prefers-color-scheme: dark)"
          data-react-helmet="true"
        />
        <meta data-rh="true" name="theme-color" content="#111827" />
        <meta name="msapplication-TileColor" content="#5b5b5b" />
        <meta name="msapplication-TileImage" content={siteConfig.touchIcon} />
        <meta name="msapplication-tooltip" content={siteConfig.title} />
        <link
          rel="search"
          type="application/opensearchdescription+xml"
          href="/opensearch.xml"
          title={siteConfig.siteName}
        />
      </SeoHead>
      <Script
        strategy="afterInteractive"
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
      />
      <Context.Provider value={{ genre, siteConfig }}>
        <SWRConfig value={{ fallback, revalidateIfStale: false }}>
          {Component.getLayout && !pageProps.statusCode ? (
            Component.getLayout(<Component {...otherProps} />)
          ) : (
            <Layout>
              <Component {...otherProps} />
            </Layout>
          )}
        </SWRConfig>
      </Context.Provider>
    </>
  );
};

MyApp.getInitialProps = async function (context: AppContext) {
  App.getInitialProps(context);

  const { data } = await get<GenreItem[]>("/api/genre/list");

  return {
    pageProps: {
      genre: data,
      fallback: { "/api/genre/list": data },
      siteConfig: getSiteConfig(context.ctx.req?.headers.host),
    },
  };
};

export default MyApp;
