import { SWRConfig } from "swr";
import Script from "next/script";
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 getSiteConfig, { SiteConfig } from "libs/getSiteConfig";

import "styles/globals.scss";
import { SeoHead, SeoHeadConfig } from "components/SeoHead";

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

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
  pageProps: {
    fallback?: {
      [key: string]: any;
    };
    siteConfig: SiteConfig;
    [key: string]: any;
  };
};

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),
    };
  }, [siteConfig.description, siteConfig.host, siteConfig.jsonLd, siteConfig.keywords, siteConfig.title]);

  useEffect(() => {
    const handleRouteChange = (url: string) => {
      pageview(url);
    };
    router.events.on("routeChangeComplete", handleRouteChange);
    router.events.on("hashChangeComplete", handleRouteChange);
    return () => {
      router.events.off("routeChangeComplete", handleRouteChange);
      router.events.off("hashChangeComplete", handleRouteChange);
    };
  }, [router.events]);

  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"
        />
        <title>{siteConfig.title}</title>
        <meta name="description" content={siteConfig.description} />
        <meta name="keywords" content={siteConfig.keywords} />
        <meta
          property="og:url"
          key="og:url"
          content={`https://${siteConfig.host}`}
        />
        <meta
          property="og:site_name"
          key="og:site_name"
          content={siteConfig.siteName}
        />
        <meta property="og:title" content={siteConfig.title} key="og:title" />
        <meta
          property="og:description"
          key="og:description"
          content={siteConfig.description}
        />
        <meta
          property="og:image"
          key="og:image"
          content={siteConfig.touchIcon}
        />
        <meta
          name="twitter:title"
          key="twitter:title"
          content={siteConfig.title}
        />
        <meta
          name="twitter:description"
          key="twitter:description"
          content={siteConfig.description}
        />
        <meta name="twitter:card" key="twitter:card" content="summary" />
        <meta
          name="twitter:image"
          key="twitter:image"
          content={siteConfig.touchIcon}
        />
        <link
          rel="canonical"
          key="canonical"
          href={`https://${siteConfig.host}`}
        />
        <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} />
        <script
          type="application/ld+json"
          key="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(siteConfig.jsonLd),
          }}
        />
      </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;
