import React, { Suspense } from 'react';
import { getIronSession, IronSession, IronSessionData } from 'iron-session';
import { GetServerSideProps } from 'next';
import dynamic from 'next/dynamic';
import Script from 'next/script';
import { ParsedUrlQuery } from 'querystring';

import { GRAPHQL_URI } from '@/modules/@common/request/constants';
import { TOKEN_COOKIE_NAME, TOKEN_COOKIE_VALUE } from '@/modules/@common/token/constants';
import { ACCOUNT, EXTERNAL, INTERNAL } from '@/modules/@common/user/constants/rolesType';
import {
  MY_WATCH_KEY,
  MY_WATCH_TITLE,
  SHARED_WITH_YOU_KEY,
  SHARED_WITH_YOU_TITLE_FOR_ACCOUNT_ROLE_TYPE,
  SHARED_WITH_YOU_TITLE_FOR_EXTERNAL_ROLE_TYPE,
} from '@/modules/hostedWatch/constants';

import { UserType } from '@/modules/@common/user/types';
import { HostedWatchInitialData, Hub, Watch, Watches } from '@/modules/hostedWatch/types';

import { setInternalWatchData } from '@/modules/@common/watch/helpers';

import Layout from '@/modules/layout';
import { getSessionOptionsWithRequest } from '../../session';

const HostedWatch = dynamic(() => import('@/modules/hostedWatch/Watches'), {
  ssr: false,
});

const hubsQueryGQL = `
query hostedWatchHubs(
  $watchId: Int
) {
  hostedWatchHubs(
    watchId: $watchId
  ) {
    id
    name
  }
}`;
const watchesQueryGQL = `
query hostedWatches(
  $publisherId: Int
  $watchId: Int
) {
  hostedWatches(
    publisherId: $publisherId
    watchId: $watchId
  ) {
    page
    pageSize
    recordsTotal
    records {
      id
      title
      folderName
      watchChannels {
        id
        title
      }
    }
  }
}`;
const myWatchQueryGQL = `
query myHostedWatch {
  myHostedWatch {
    id
    title
    folderName
    watchChannels {
      id
      title
    }
  }
}`;
const externalShareWatchQueryGQL = `
query hostedStaticWatches {
  hostedStaticWatches {
    externalShareId
    externalShareFolderName
  }
}`;

interface Params extends ParsedUrlQuery {
  params: [string];
}

const fetchInstance =
  (token: string, cookie: string = '') =>
  (query: string, variables: object = {}) =>
    fetch(`${process.env.NEXT_HOST}${GRAPHQL_URI}`, {
      headers: {
        Authorization: token,
        Cookie: cookie,
        'Content-Type': 'application/json',
      },
      method: 'POST',
      body: JSON.stringify({
        query,
        variables,
      }),
    }).then((res) => res.json());

export const getServerSideProps: GetServerSideProps = async ({ req, res, params }) => {
  const session: IronSession<IronSessionData> = await getIronSession(req, res, getSessionOptionsWithRequest(req));
  const { params: [urlWatchParam = ''] = [] } = params as Params;

  const responseData = await fetch(`${process.env.NEXT_HOST}/api/auth/init?token=${session.user.token}`).then(
    (rawResponse) => rawResponse.json(),
  );
  const userData = responseData.user.user;

  const accountLogoUrl = userData.account?.avatarUrl ?? null;
  const accountUrl = userData.account?.customLogoUrl ?? null;

  const fetchData = fetchInstance(
    session.user.token,
    `${session.user[TOKEN_COOKIE_NAME]}=${session.user[TOKEN_COOKIE_VALUE]}`,
  );

  let hubs = [] as Array<Hub>;
  let watches = {} as Watches;
  let myWatch = {} as Watch;
  let shared = {} as Watch;
  let selectedWatch = {} as Watch;
  let redirectWatchId = '';

  if ([INTERNAL, ACCOUNT].includes(userData.role.type)) {
    const watchId = urlWatchParam && parseInt(urlWatchParam, 10) ? parseInt(urlWatchParam, 10) : undefined;

    const { data: { hostedWatchHubs = [] } = {} } = await fetchData(hubsQueryGQL, {
      watchId,
    });
    hubs = hostedWatchHubs.map(({ id, name }: { id: number; name: string }) => ({
      id,
      title: name,
    }));

    if (hubs.length) {
      const { data: { hostedWatches = {} } = {} } = await fetchData(watchesQueryGQL, {
        publisherId: hubs[0].id,
        watchId,
      });
      watches = hostedWatches;
    }

    if (userData.role.type === ACCOUNT) {
      const { data: { hostedStaticWatches = {} } = {} } = await fetchData(externalShareWatchQueryGQL);
      shared = {
        id: hostedStaticWatches.externalShareId,
        title: SHARED_WITH_YOU_TITLE_FOR_ACCOUNT_ROLE_TYPE,
        folderName: hostedStaticWatches.externalShareFolderName,
      };

      const { data: { myHostedWatch = {} } = {} } = await fetchData(myWatchQueryGQL);
      myWatch = {
        ...myHostedWatch,
        title: MY_WATCH_TITLE,
      };
    }
  } else if (userData.role.type === EXTERNAL) {
    const { data: { hostedStaticWatches = {} } = {} } = await fetchData(externalShareWatchQueryGQL);
    shared = {
      id: hostedStaticWatches.externalShareId,
      title: SHARED_WITH_YOU_TITLE_FOR_EXTERNAL_ROLE_TYPE,
      folderName: hostedStaticWatches.externalShareFolderName,
    };
  }

  // check if myWatch or sharedWithYou was requested
  if (myWatch?.id && [MY_WATCH_KEY, `${myWatch.id}`].includes(urlWatchParam)) {
    selectedWatch = myWatch;
    redirectWatchId = MY_WATCH_KEY;
  } else if (shared?.id && [SHARED_WITH_YOU_KEY, `${shared.id}`].includes(urlWatchParam)) {
    selectedWatch = shared;
    redirectWatchId = SHARED_WITH_YOU_KEY;
  }

  // if nothing was requested trying to open some watch -> sharedWithYou
  if (!redirectWatchId && watches?.records?.length) {
    selectedWatch = {
      id: watches.records[0].id,
      title: watches.records[0].title,
      folderName: watches.records[0].folderName,
    };
    redirectWatchId = selectedWatch.id.toString();
  } else if (!redirectWatchId && myWatch?.id) {
    selectedWatch = myWatch;
    redirectWatchId = MY_WATCH_KEY;
  } else if (!redirectWatchId && shared?.id) {
    selectedWatch = shared;
    redirectWatchId = SHARED_WITH_YOU_KEY;
  }

  return {
    props: {
      user: userData,
      token: session.user.token,
      hostedWatchInitialData: {
        accountLogoUrl,
        accountUrl,
        hubs,
        selectedHub: hubs.length ? hubs[0] : {},
        watches,
        myWatch,
        shared,
        selectedWatch,
        redirectWatchId: urlWatchParam !== redirectWatchId ? redirectWatchId : null,
      },
    },
  };
};

interface HomeProps {
  token: string;
  user: UserType;
  hostedWatchInitialData: HostedWatchInitialData;
}

export default function Home(props: HomeProps) {
  return (
    <Layout>
      <Script
        dangerouslySetInnerHTML={{
          __html: setInternalWatchData({ token: props.token, user: props.user }),
        }}
        strategy="beforeInteractive"
      />
      <Suspense fallback={null}>
        <HostedWatch {...props.hostedWatchInitialData} />
      </Suspense>
    </Layout>
  );
}
