/* eslint-disable @next/next/no-img-element */
import { useEffect, useRef, useState } from 'react';
import getRawBody from 'raw-body';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import type { OIDCSSORecord, ProductConfig, SAMLSSORecord } from '@boxyhq/saml-jackson';
import type { InferGetServerSidePropsType } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import jackson from '@lib/jackson';
import Head from 'next/head';
import { hexToOklch } from '@lib/color';
import { PoweredBy } from '@components/PoweredBy';
import { getPortalBranding, getProductBranding } from '@ee/branding/utils';
import { boxyhqHosted } from '@lib/env';

interface Connection {
  name: string;
  product: string;
  clientID: string;
  sortOrder: number | null;
  deactivated: boolean;
}

export default function ChooseIdPConnection({
  connections,
  SAMLResponse,
  authFlow,
  branding,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  const { t } = useTranslation('common');

  const primaryColor = hexToOklch(branding.primaryColor);
  const title = authFlow === 'sp-initiated' ? t('select_an_idp') : t('select_an_app');

  const selectors = {
    'sp-initiated': <IdpSelector connections={connections} />,
    'idp-initiated': <AppSelector connections={connections} SAMLResponse={SAMLResponse} />,
  };

  return (
    <div className='mx-auto my-28 w-[500px]'>
      <div className='mx-5 flex flex-col space-y-10 rounded border border-gray-300 p-10'>
        <Head>
          <title>{`${title} - ${branding.companyName}`}</title>
          {branding?.faviconUrl && <link rel='icon' href={branding.faviconUrl} />}
        </Head>

        {primaryColor && <style>{`:root { --p: ${primaryColor}; }`}</style>}

        {branding?.logoUrl && (
          <div className='flex justify-center'>
            <img src={branding.logoUrl} alt={branding.companyName} className='max-h-14' />
          </div>
        )}

        {authFlow in selectors ? (
          selectors[authFlow]
        ) : (
          <p className='text-center text-sm text-slate-600'>{t('invalid_request_try_again')}</p>
        )}
      </div>
      <div className='my-4'>
        <PoweredBy />
      </div>
    </div>
  );
}

const IdpSelector = ({ connections }: { connections: Connection[] }) => {
  const router = useRouter();
  const { t } = useTranslation('common');

  // SP initiated SSO: Redirect to the same path with idp_hint set to the selected connection clientID
  const connectionSelected = (clientID: string) => {
    return router.push(`${router.asPath}&idp_hint=${clientID}`);
  };

  return (
    <>
      <h3 className='text-center text-xl font-bold'>{t('select_an_idp')}</h3>
      <ul className='flex flex-col space-y-5'>
        {connections.map((connection) => {
          return (
            <li key={connection.clientID} className='rounded bg-gray-100'>
              <button
                type='button'
                className='w-full'
                onClick={() => {
                  connectionSelected(connection.clientID);
                }}>
                <div className='flex items-center gap-2 px-3 py-3'>
                  <div className='placeholder avatar'>
                    <div className='w-8 rounded-full bg-primary text-white'>
                      <span className='text-lg font-bold'>{connection.name.charAt(0).toUpperCase()}</span>
                    </div>
                  </div>
                  {connection.name}
                </div>
              </button>
            </li>
          );
        })}
      </ul>
      <p className='text-center text-sm text-slate-600'>{t('choose_an_identity_provider_to_continue')}</p>
    </>
  );
};

const AppSelector = ({
  connections,
  SAMLResponse,
}: {
  connections: Connection[];
  SAMLResponse: string | null;
}) => {
  const { t } = useTranslation('common');
  const formRef = useRef<HTMLFormElement>(null);
  const [connection, setConnection] = useState<string | null>(null);

  // Warn the user if they refresh the page
  useEffect(() => {
    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
      if (!connection) {
        e.preventDefault();
        e.returnValue = '';
        return '';
      }
    };

    if (!connection) {
      window.addEventListener('beforeunload', handleBeforeUnload);
    }

    return () => {
      window.removeEventListener('beforeunload', handleBeforeUnload);
    };
  }, [connection]);

  // IdP initiated SSO: Submit the SAMLResponse and idp_hint to the SAML ACS endpoint
  const appSelected = (e: React.ChangeEvent<HTMLInputElement>) => {
    setConnection(e.target.value);
    formRef.current?.submit();
  };

  if (!SAMLResponse) {
    return <p className='text-center text-sm text-slate-600'>{t('no_saml_response_try_again')}</p>;
  }

  return (
    <>
      <h3 className='text-center text-xl font-bold'>{t('select_an_app')}</h3>
      <form method='POST' action='/api/oauth/saml' ref={formRef}>
        <input type='hidden' name='SAMLResponse' value={SAMLResponse} />
        <ul className='flex flex-col space-y-5'>
          {connections.map((connection) => {
            return (
              <li key={connection.clientID} className='rounded bg-gray-100'>
                <div className='flex items-center gap-2 px-3 py-3'>
                  <input
                    type='radio'
                    name='idp_hint'
                    className='radio'
                    value={connection.clientID}
                    onChange={appSelected}
                    id={connection.clientID}
                  />
                  <label htmlFor={connection.clientID}>{connection.product}</label>
                </div>
              </li>
            );
          })}
        </ul>
      </form>
      <p className='text-center text-sm text-slate-600'>{t('choose_an_app_to_continue')}</p>
    </>
  );
};

export const getServerSideProps = async ({ query, locale, req }) => {
  const { connectionAPIController, samlFederatedController, checkLicense, productController } =
    await jackson();

  const paramsToRelay = { ...query } as { [key: string]: string };

  const { authFlow, entityId, tenant, product, idp_hint, samlFedAppId, fedType } = query as {
    authFlow: 'sp-initiated' | 'idp-initiated';
    tenant?: string;
    product?: string;
    idp_hint?: string;
    entityId?: string;
    samlFedAppId?: string;
    fedType?: string;
  };

  if (!['sp-initiated', 'idp-initiated'].includes(authFlow)) {
    return {
      notFound: true,
    };
  }

  // The user has selected an IdP to continue with
  if (idp_hint) {
    const params = new URLSearchParams(paramsToRelay);
    const destination =
      samlFedAppId && fedType !== 'oidc'
        ? `/api/federated-saml/sso?${params}`
        : `/api/oauth/authorize?${params}`;

    return {
      redirect: {
        destination,
        permanent: false,
      },
    };
  }

  // SAML federated app
  const samlFederationApp = samlFedAppId ? await samlFederatedController.app.get({ id: samlFedAppId }) : null;

  if (samlFedAppId && !samlFederationApp) {
    return {
      notFound: true,
    };
  }

  // Otherwise, show the list of IdPs
  let connections: (OIDCSSORecord | SAMLSSORecord)[] = [];

  if (samlFederationApp) {
    const tenants = samlFederationApp?.tenants || [samlFederationApp.tenant];
    const { product } = samlFederationApp;

    connections = await connectionAPIController.getConnections({ tenant: tenants, product, sort: true });
  } else if (tenant && product) {
    connections = await connectionAPIController.getConnections({ tenant, product, sort: true });
  } else if (entityId) {
    connections = await connectionAPIController.getConnections({ entityId: decodeURIComponent(entityId) });
  }

  // Get the branding to use for the IdP selector screen
  let branding = boxyhqHosted && product ? await getProductBranding(product) : await getPortalBranding();

  // For SAML federated requests, use the branding from the SAML federated app
  if (samlFederationApp && (await checkLicense())) {
    branding = {
      logoUrl: samlFederationApp?.logoUrl || branding.logoUrl,
      primaryColor: samlFederationApp?.primaryColor || branding.primaryColor,
      faviconUrl: samlFederationApp?.faviconUrl || branding.faviconUrl,
      companyName: samlFederationApp?.name || branding.companyName,
    };
  }

  let connectionsTransformed: Connection[] = connections.map((connection) => {
    const idpMetadata = 'idpMetadata' in connection ? connection.idpMetadata : undefined;
    const oidcProvider = 'oidcProvider' in connection ? connection.oidcProvider : undefined;

    const name =
      connection.name ||
      (idpMetadata ? idpMetadata.friendlyProviderName || idpMetadata.provider : `${oidcProvider?.provider}`);

    return {
      name,
      product: connection.product,
      clientID: connection.clientID,
      sortOrder: connection.sortOrder || null,
      deactivated: connection.deactivated || false,
    };
  });

  // Filter out connections that are not enabled
  connectionsTransformed = connectionsTransformed.filter((connection) => connection.deactivated !== true);

  // For idp-initiated flows, we need to parse the SAMLResponse from the request body and pass it to the component
  if (req.method == 'POST') {
    const body = await getRawBody(req);
    const params = new URLSearchParams(body.toString('utf-8'));

    const SAMLResponse = params.get('SAMLResponse');

    // SAMLResponse should exist with idp-initiated flow
    if (!SAMLResponse) {
      return {
        notFound: true,
      };
    }

    if (boxyhqHosted) {
      // Fetch products to display the product name instead of the product ID
      const products = (await Promise.allSettled(
        connectionsTransformed.map((connection) => productController.get(connection.product))
      )) as PromiseFulfilledResult<ProductConfig>[];

      connectionsTransformed = connectionsTransformed.map((connection, index) => {
        if (products[index].status === 'fulfilled') {
          return {
            ...connection,
            product: products[index].value.name || connection.product,
          };
        }

        return connection;
      });
    }

    return {
      props: {
        ...(locale ? await serverSideTranslations(locale, ['common']) : {}),
        authFlow,
        SAMLResponse,
        connections: connectionsTransformed,
        branding,
      },
    };
  }

  return {
    props: {
      ...(locale ? await serverSideTranslations(locale, ['common']) : {}),
      authFlow,
      SAMLResponse: null,
      connections: connectionsTransformed,
      branding,
    },
  };
};
