import '@/mui/theme.global.scss';
// to set store before any actions
import '@/modules/@common/store/store';

import React, { useEffect, useState } from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import { getIronSession, IronSession, IronSessionData } from 'iron-session';
import App, { AppContext, AppProps } from 'next/app';
import Head from 'next/head';
import createCache from '@emotion/cache';
import type { EmotionCache } from '@emotion/react';
import { AppCacheProvider } from '@mui/material-nextjs/v15-pagesRouter';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import type { Store } from '@reduxjs/toolkit';

import { LOGIN_PAGE } from '@/modules/@common/router/constants';
import { INTERNAL } from '@/modules/@common/user/constants/rolesType';
import { APPEARANCE_MODE_DARK, APPEARANCE_MODE_LIGHT } from '@/mui/constants';

import { UserType } from '@/modules/@common/user/types';

import '@/mui/helpers/license';
import getRoutesAllowed from '@/modules/@common/router/helpers/getRoutesAllowed';
import initUser from '@/modules/@common/user/helpers/initUser';
import { getSettings } from '@/mui/helpers/cookie';

import ErrorBoundary from '@/modules/@common/app/ErrorBoundary';
import GoogleAnalyticsProvider from '@/modules/@common/app/GoogleAnalyticsProvider';
import IntercomProvider from '@/modules/@common/app/IntercomProvider';
import RecordingProvider from '@/modules/@common/app/RecordingProvider';
import { SettingsProvider } from '@/modules/@common/app/SettingsProvider';
import store from '@/modules/@common/store';
import ThemeConfig from '@/mui/theme';
import { getSessionOptionsWithRequest } from '../session';
import { CssBaseline, SnackbarGroup } from '@/mui/components';

import androidChrome from '@/assets/img/favicon/android-chrome-192x192-1621329404738.png';
import androidChromeX2 from '@/assets/img/favicon/android-chrome-512x512-1621329404738.png';
import appleTouchIcon from '@/assets/img/favicon/apple-touch-icon-1621329404738.png';
import fav from '@/assets/img/favicon/favicon-16x16-1621329404738.png';
import favX2 from '@/assets/img/favicon/favicon-32x32-1621329404738.png';

interface MyAppProps extends AppProps {
  authData: {
    token: string;
    impersonated: boolean;
    user: {
      user: UserType;
      iab: {
        data: string;
      };
      videoLangs: Array<{ id: string; name: string }>;
    };
  };
  settings: {
    themeMode: typeof APPEARANCE_MODE_LIGHT | typeof APPEARANCE_MODE_DARK;
  };
  emotionCache: EmotionCache;
}

function createEmotionCache() {
  let insertionPoint: HTMLElement | undefined;

  if (typeof document !== 'undefined') {
    insertionPoint = (document.querySelector('meta[name="emotion-insertion-point"]') as HTMLElement) ?? undefined;
  }

  return createCache({
    key: 'css',
    prepend: true,
    insertionPoint,
  });
}

const clientCache = createEmotionCache();

function MyApp({ authData, settings, ...props }: MyAppProps) {
  const [isHydrated, setHydrated] = useState<boolean>(false);

  if (authData?.token) {
    initUser(authData);
  }

  const { Component, pageProps, emotionCache } = props;

  useEffect(() => setHydrated(true), []);

  return (
    <AppCacheProvider emotionCache={emotionCache ?? clientCache}>
      <Head>
        <title>Video Manager</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        {isHydrated && <meta name="next" content="hydrated" />}
        <link rel="apple-touch-icon" sizes="180x180" href={appleTouchIcon.src} />
        <link rel="icon" type="image/png" sizes="16x16" href={fav.src} />
        <link rel="icon" type="image/png" sizes="32x32" href={favX2.src} />
        <link rel="icon" type="image/png" sizes="192x192" href={androidChrome.src} />
        <link rel="icon" type="image/png" sizes="512x512" href={androidChromeX2.src} />
      </Head>
      <ReduxProvider store={store.current as Store}>
        <SettingsProvider defaultSettings={settings}>
          <ThemeConfig global>
            <CssBaseline />
            <LocalizationProvider dateAdapter={AdapterDayjs}>
              <ErrorBoundary>
                <IntercomProvider>
                  <RecordingProvider>
                    <GoogleAnalyticsProvider>
                      <Component {...pageProps} />
                      <SnackbarGroup maxSnack={4} />
                    </GoogleAnalyticsProvider>
                  </RecordingProvider>
                </IntercomProvider>
              </ErrorBoundary>
            </LocalizationProvider>
          </ThemeConfig>
        </SettingsProvider>
      </ReduxProvider>
    </AppCacheProvider>
  );
}

MyApp.getInitialProps = async (appContext: AppContext) => {
  const appProps = await App.getInitialProps(appContext);

  let authData = null;

  if (appContext.ctx.req && appContext.ctx.res) {
    const session: IronSession<IronSessionData> = await getIronSession(
      appContext.ctx.req,
      appContext.ctx.res,
      getSessionOptionsWithRequest(appContext.ctx.req),
    );

    const [mainSubdomain] = process.env.APP_ENV_BASE_URL?.split('//')[1].split('.') || [];
    const [customSubdomain] = appContext.ctx.req.headers.host?.split('.') || [];

    const { user } = session;

    if (user?.token && user?.origin === process.env.APP_ENV_BASE_URL) {
      let response;
      try {
        const res = await fetch(`${process.env.NEXT_HOST}/api/auth/init?token=${user.token}`);

        if (!res.ok) {
          throw new Error(`Failed to fetch user data: ${res.statusText}`);
        }

        response = await res.json();

        const {
          user: { user: userData, iab, videoLangs },
        } = response;

        // update allowed routes each time page refreshed
        session.user.routesAllowed = getRoutesAllowed(userData?.permissions);

        await session.save();

        if (!userData || !iab) {
          throw new ReferenceError('PCN does not respond');
        }
        if (!videoLangs) {
          throw new ReferenceError('Weavo does not respond');
        }

        if (
          customSubdomain !== mainSubdomain &&
          (userData?.role?.type === INTERNAL || userData?.account?.subdomain !== customSubdomain)
        ) {
          // Subdomain redirect
          appContext.ctx.res.writeHead(302, {
            Location: `${process.env.APP_ENV_BASE_URL}${appContext.ctx.req.url}`,
            Error: 'Forbidden domain',
          });

          appContext.ctx.res.end();
        }
      } catch (e: unknown) {
        response = null;
        session.destroy();

        if (e instanceof Error) {
          appContext.ctx.res.writeHead(302, {
            Location: LOGIN_PAGE.path,
            Error: `${e.name}: ${e.message}`,
          });
        }

        appContext.ctx.res.end();
      }

      authData = {
        ...response,
        impersonated: !!user?.impersonateToken,
      };
    } else {
      let isSubdomainExist;
      // to prevent somebody calls api from inside host 10.10...
      if (customSubdomain !== '10') {
        try {
          const res = await fetch(`${process.env.APP_PCN_API_BASE_URL_FE}/public/accounts/domain/${customSubdomain}`);
          isSubdomainExist = res.ok ? res : await res.json();
        } catch {
          isSubdomainExist = null;
        }
      }

      if (customSubdomain !== mainSubdomain && isSubdomainExist?.status !== 200) {
        // Subdomain redirect
        appContext.ctx.res.writeHead(302, {
          Location: `${process.env.APP_ENV_BASE_URL}${appContext.ctx.req.url}`,
          Error: `${isSubdomainExist?.message}`,
        });

        appContext.ctx.res.end();
      }
    }
  }

  return {
    ...appProps,
    authData,
    settings: getSettings(appContext.ctx.req ? appContext.ctx.req.headers.cookie || '' : document.cookie),
  };
};

export default MyApp;
