import * as React from 'react';

import {
  config as fontawesomeSvgCoreConfig,
  library,
} from '@fortawesome/fontawesome-svg-core';
import {
  faClock,
  faCopy,
  faDownload,
  faHourglass,
  faLanguage,
  faLightbulb,
  faSearch,
  faShare,
} from '@fortawesome/free-solid-svg-icons';
import { faLightbulb as farLightbulb } from '@fortawesome/free-regular-svg-icons';
import { faTwitch as fabTwitch } from '@fortawesome/free-brands-svg-icons';
import '@fortawesome/fontawesome-svg-core/styles.css';

import Container from 'react-bootstrap/Container';
import Navbar from 'react-bootstrap/Navbar';

import { IntlProvider, FormattedMessage } from 'react-intl';

import Link from 'next/link';
import Router from 'next/router';
import App, { AppContext } from 'next/app';

import useSWR, { SWRConfig } from 'swr';

import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

import '../styles/main.scss';
import Image from 'react-bootstrap/Image';
import Head from 'next/head';
import { ButtonGroup } from 'react-bootstrap';
import { shouldPolyfill } from '@formatjs/intl-numberformat/should-polyfill';
import HomeStyle from '../styles/Home.module.css';
import DarkToggler from '../components/DarkToggler';

import { fetchJson } from '../util/api';
import { defaultLocale, loadLocaleData, loadLocalePolyfill } from '../util/localization';

import gdqLogo from '../images/gdqlogo.png';
import favicon from '../images/favicon.svg';
import LocaleSwitcher from '../components/LocaleSwitcher';
import withSession from '../util/session';

fontawesomeSvgCoreConfig.autoAddCss = false;

library.add(
  fabTwitch,
  faClock,
  faCopy,
  faDownload,
  faHourglass,
  faLanguage,
  faLightbulb,
  farLightbulb,
  faSearch,
  faShare,
);

Router.events.on('routeChangeStart', (url) => {
  console.log(`Loading: ${url}`);
  NProgress.start();
});
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());

export interface GDQArchiveAppPageProps extends Record<string, any> {
  locale: string,
  messages: any,
  enableDark: boolean,
}

const polyfillRequired = shouldPolyfill();

function GDQArchiveApp({
  Component,
  pageProps: {
    initialEnableDark,
    initialLocale,
    initialMessages,
    ...pageProps
  },
}: {
  Component: React.ElementType<Record<string, any>>,
  pageProps: GDQArchiveAppPageProps,
}) {
  /* Component state */

  const [currentMessages, setMessages] = React.useState(initialMessages);
  const [isChangingLocale, setIsChangingLocale] = React.useState(false);
  const [isChangingDarkMode, setIsChangingDarkMode] = React.useState(false);
  const [{
    loadedPolyfill,
    isLoadingPolyfill,
  }, setPolyfillState] = React.useState({
    loadedPolyfill: null,
    isLoadingPolyfill: false,
  });

  /* Synchronize via SWR */

  const {
    data: userData,
    error,
    isValidating,
    mutate,
  } = useSWR('/api/user', {
    fetcher: fetchJson,
    initialData: {
      locale: initialLocale,
      enableDark: initialEnableDark,
    },
    onError(err) {
      console.log(err);
    },
  });
  if (error) {
    console.log(error);
  }

  const {
    enableDark: enableDarkMode,
    locale: currentLocale,
  } = userData;

  /* Polyfill for locale */

  if (!isLoadingPolyfill
    && polyfillRequired
    && currentLocale !== loadedPolyfill) {
    console.log('load locale polyfill start');
    loadLocalePolyfill(currentLocale)
      .then(() => {
        console.log('set polyfill state end');
        setPolyfillState({
          loadedPolyfill: currentLocale,
          isLoadingPolyfill: false,
        });
      });
  }

  async function onChangeLocale(value: string) {
    setIsChangingLocale(true);

    const formData = new URLSearchParams();
    formData.append('locale', value);

    await fetchJson('/api/changePreferences', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: formData,
    });

    mutate({ ...userData, locale: value });

    setMessages(await loadLocaleData(value));
    setIsChangingLocale(false);
  }

  let darkToggleAnimationTimer: NodeJS.Timeout = null;

  function triggerDarkToggleAnimation() {
    document.documentElement.setAttribute('data-toggled-dark', 'true');
    if (darkToggleAnimationTimer !== null) {
      clearTimeout(darkToggleAnimationTimer);
    }
    darkToggleAnimationTimer = setTimeout(() => {
      document.documentElement.removeAttribute('data-toggled-dark');
    }, 1200);
  }

  /**
 * @param {bool} value
 */
  async function onChangeDarkMode(value: boolean) {
    setIsChangingDarkMode(true);

    const formData = new URLSearchParams();
    formData.append('enableDark', value.toString());

    await fetchJson('/api/changePreferences', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: formData,
    });

    mutate({ ...userData, enableDark: value });

    triggerDarkToggleAnimation();
    setIsChangingDarkMode(false);
  }

  React.useEffect(() => {
    if (enableDarkMode) {
      document.documentElement.setAttribute('data-enable-dark', 'true');
    } else {
      document.documentElement.removeAttribute('data-enable-dark');
    }
    document.documentElement.setAttribute('lang', currentLocale);
  });

  return (
    <SWRConfig
      value={{
        fetcher: fetchJson,
        onError(err) {
          console.error(err);
        },
      }}
    >
      <IntlProvider
        messages={currentMessages}
        locale={currentLocale}
        defaultLocale={defaultLocale}
      >
        <Navbar bg="dark" variant="dark">
          <Link passHref href="/">
            <Navbar.Brand>
              <Image
                src={gdqLogo}
                alt="Games Done Quick"
                className={[
                  HomeStyle.logo,
                  'd-inline-block',
                  'align-middle',
                ].join(' ')}
              />
              {' '}
              <FormattedMessage
                id="Navbar.brandText"
                defaultMessage="Instant Archive"
              />
            </Navbar.Brand>
          </Link>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse
            id="basic-navbar-nav"
            className="justify-content-end"
          >
            <ButtonGroup>
              <LocaleSwitcher
                locale={currentLocale}
                onChangeLocale={onChangeLocale}
                disabled={isChangingLocale || isValidating}
                showLoading={isChangingLocale}
              />
              <DarkToggler
                isDarkEnabled={enableDarkMode}
                onChangeDarkMode={onChangeDarkMode}
                disabled={isChangingDarkMode || isValidating}
                showLoading={isChangingDarkMode}
              />
            </ButtonGroup>
          </Navbar.Collapse>
        </Navbar>
        <Head>
          <link rel="icon" type="image/svg+xml" href={favicon} />
        </Head>
        <Container>
          {/* eslint-disable-next-line react/jsx-props-no-spreading */}
          <Component {...pageProps} />
        </Container>
      </IntlProvider>
    </SWRConfig>
  );
}

const getInitialPropsFromReq = withSession(async (req) => {
  const initialLocale = req.session.get('locale') || defaultLocale;
  const initialMessages = await loadLocaleData(initialLocale);
  const initialEnableDark = req.session.get('enable-dark') || false;

  return {
    pageProps: {
      initialEnableDark,
      initialLocale,
      initialMessages,
    },
  };
});

GDQArchiveApp.getInitialProps = async (appContext: AppContext) => {
  const result = ({
    ...await App.getInitialProps(appContext),
    ...await getInitialPropsFromReq(appContext.ctx.req, appContext.ctx.res),
  });
  // console.log('getInitialProps for app:', result);
  return result;
};

export default GDQArchiveApp;
