import React, { useRef, useState } from 'react';
import defaultsDeep from 'lodash/defaultsDeep';
import PropTypes from 'prop-types';

import Badge from '@leafygreen-ui/badge';
import Button from '@leafygreen-ui/button';
import { css, cx } from '@leafygreen-ui/emotion';
// @ts-expect-error
import ArrowRightIcon from '@leafygreen-ui/icon/dist/ArrowRight';
// @ts-expect-error
import BellIcon from '@leafygreen-ui/icon/dist/Bell';
import { useDarkMode } from '@leafygreen-ui/leafygreen-provider';
import { Theme } from '@leafygreen-ui/lib';
import { MongoDBLogoMark } from '@leafygreen-ui/logo';
import {
  FocusableMenuItem,
  Menu,
  MenuItem,
  MenuSeparator,
  SubMenu,
} from '@leafygreen-ui/menu';
import { Link } from '@leafygreen-ui/typography';

import { hostDefaults } from '../../data';
import {
  CloudIcon,
  DevHubIcon,
  DocsIcon,
  ForumsIcon,
  MegaphoneIcon,
  SupportIcon,
  UniversityIcon,
} from '../../icons';
import { useOnElementClick } from '../../on-element-click-provider';
import {
  AccountInterface,
  ActiveNavElement,
  Environment,
  HostsInterface,
  NavElement,
  Platform,
  URLS,
  UserMenuURLS,
} from '../../types';

import {
  descriptionStyle,
  headerBaseStyle,
  headerThemeStyle,
  linkColorStyle,
  logoMarkBaseStyle,
  logoMarkThemeStyle,
  menuBaseStyle,
  menuGlyphThemeStyle,
  menuThemeStyle,
  mfaBannerBaseStyle,
  mfaBannerThemeStyle,
  nameStyle,
  productLinkBaseStyle,
  productLinkIconStyle,
  productLinkThemeStyle,
  subMenuActiveContainerStyle,
  subMenuClassName,
  subMenuContainerBaseStyle,
  subMenuContainerThemeStyle,
  subMenuItemStyle,
  truncate,
} from './UserMenu.style';
import { UserMenuTrigger } from './UserMenuTrigger';

type PolyProps = { as: 'a'; href: string } | { as: 'button' };

type ProductString =
  | 'cloud'
  | 'university'
  | 'support'
  | 'developer'
  | 'docs'
  | 'forums';
type DescriptionProps = {
  isActive: boolean;
  isGovernment?: boolean;
  theme: Theme;
} & (
  | {
      /** Required if product is not provided */
      link?: string;
      /** Required if link is not provided */
      product: ProductString;
    }
  | {
      /** Required if product is not provided */
      link: string;
      /** Required if link is not provided */
      product?: ProductString;
    }
);

function UserMenuItemDescription({
  product,
  link,
  isGovernment = false,
  theme,
}: DescriptionProps) {
  if (!link) {
    if (
      isGovernment &&
      ['cloud', 'support'].includes(product as ProductString)
    ) {
      link = `${product}.mongodbgov.com`;
    } else {
      link = `${product}.mongodb.com`;
    }
  } else {
    link = link.replace(/https?:\/\//, ''); // replace any http in the provided link
  }

  return (
    <div
      aria-hidden
      className={cx(productLinkBaseStyle, productLinkThemeStyle[theme])}
    >
      {link}
      <ArrowRightIcon
        size="small"
        className={productLinkIconStyle}
        role="presentation"
      />
    </div>
  );
}

export interface UserMenuProps {
  /**
   * Object that contains information about the active user.
   * {firstName: 'string', lastName: 'string', email: 'string'}
   */
  account?: AccountInterface;

  /**
   * Determines what nav item is currently active.
   */
  activeNav?: NavElement;

  /**
   * Callback invoked after the user clicks log out.
   */
  onLogout?: React.MouseEventHandler;

  /**
   * Callback invoked after the user clicks a product.
   */
  onProductChange?: React.MouseEventHandler;

  /**
   * Object that supplies URL overrides to UserMenu component.
   * Shape: { userMenu:{ cloud: { userPreferences, organizations, invitations, mfa }, university: { universityPreferences }, support: { userPreferences }, account: { homepage } }}
   */
  urls?: URLS | { userMenu: UserMenuURLS };

  /**
   * Object that supplies host overrides to UserMenu component.
   * Shape: { cloud, realm, charts, account, university, support }
   * Defaults to the production homepages of each product
   */
  hosts?: HostsInterface;

  /**
   * MongoDB platform that is currently active.
   */
  activePlatform?: Platform;

  /**
   * Describes the environment that the consumer is in: `commercial` or `government`
   */
  environment?: Environment;

  /**
   * Whether the content has been loaded
   */
  loading?: boolean;
}

/**
 * # UserMenu
 *
 * UserMenu component
 *
 * ```
<UserMenu
  account={account}
  activePlatform="cloud"
  onLogout={onLogout}
  onProductChange={onProductChange}
  urls={urls}
  hosts={hosts}
/>
```
* @param props.account Object that contains information about the active user.
*   {firstName: 'string', lastName: 'string', email: 'string'}
* @param props.activeNav Determines what nav item is currently active.
* @param props.onLogout Callback fired when a user logs out.
* @param props.onProductChange Callback invoked after the user clicks a product.
* @param props.hosts Object where keys are MDB products and values are the desired hostURL override for that product, to enable `<UserMenu />` to work across all environments.
* @param props.urls Object to enable custom overrides for every `href` used in `<UserMenu />`.
* @param props.activePlatform MongoDB platform that is currently active.
* @param props.environment Describes the environment that the consumer is in: `commercial` or `government`
*/
function UserMenu({
  account,
  activeNav,
  activePlatform,
  onLogout: onLogoutProp,
  urls: urlsProp,
  hosts: hostsProp,
  onProductChange = () => {},
  environment = Environment.Commercial,
  loading,
}: UserMenuProps) {
  const { theme, darkMode } = useDarkMode();

  const hosts: Required<HostsInterface> = defaultsDeep(
    hostsProp,
    hostDefaults(environment === Environment.Government),
  );
  const onElementClick = useOnElementClick();
  const triggerRef = useRef<HTMLButtonElement>(null);

  const onLogout = (e: React.MouseEvent) => {
    if (onLogoutProp) {
      return onLogoutProp(e);
    } else {
      return onElementClick(NavElement.Logout)(e);
    }
  };

  const defaultURLs: Required<UserMenuURLS> = {
    cloud: {
      userPreferences: `${hosts.cloud}/v2#/preferences/personalization`,
      organizations: `${hosts.cloud}/v2#/preferences/organizations`,
      invitations: `${hosts.cloud}/v2#/preferences/invitations`,
      mfa: `${hosts.cloud}/v2#/preferences/2fa`,
    },
    docs: 'https://docs.mongodb.com',
    university: {
      transcript: `${hosts.university}/learn/transcript`,
      faqs: `${hosts.university}/support`,
    },
    devHub: 'https://developer.mongodb.com',
    forums: {
      forumsPreferences:
        'https://www.mongodb.com/community/forums/my/preferences/account',
    },
    support: {
      userPreferences: `${hosts.support}/user`,
    },
    account: {
      homepage: `${hosts.account}/account/profile/overview`,
    },
    logout: `${hosts.account}/account/login?signedOut=true`,
  };

  const userMenuUrls: Required<UserMenuURLS> = defaultsDeep(
    urlsProp?.userMenu,
    defaultURLs,
  );
  const cloudUrls = userMenuUrls.cloud;

  const [open, setOpen] = useState(false);

  const name = account
    ? `${account.firstName ?? ''} ${account.lastName ?? ''}`
    : '';

  const isAccount = activePlatform === Platform.Account;
  const isCloud = activePlatform === Platform.Cloud;
  const isDocs = activePlatform === Platform.Docs;
  const isSupport = activePlatform === Platform.Support;
  const isUniversity = activePlatform === Platform.University;
  const isGovernment = environment === Environment.Government;
  const isDevHub = activePlatform === Platform.DevHub;
  const isForums = activePlatform === Platform.Forum;

  const onClick = (e: React.MouseEvent) => {
    onProductChange(e);
    setOpen(false);
  };

  const toggleOpen = (event?: React.MouseEvent) => {
    // We stop the native event from bubbling, but allow the React.Synthetic event to bubble
    // This way click handlers on parent components will still fire,
    // but this click event won't propagate up to the document and immediately close the menu.
    event?.nativeEvent?.stopPropagation?.();
    setOpen(curr => !curr);
  };

  const sharedProps = {
    target: '_blank',
    rel: 'noopener noreferrer',
  };

  const feedbackAnchorProps = {
    href: 'https://feedback.mongodb.com/',
    target: '_blank',
    rel: 'noopener noreferrer',
  };

  const ariaHiddenProps = {
    'aria-hidden': true,
    alt: '',
    role: 'presentation',
  };

  const universitySharedProps = {
    size: 'large',
    active: isUniversity,
    disabled: !account,
    href: hosts.university,
    ['data-testid']: 'user-submenu-university',
    glyph: (
      <UniversityIcon
        {...ariaHiddenProps}
        className={cx({ [menuGlyphThemeStyle[theme]]: !isUniversity })}
      />
    ),
    onClick: onElementClick(NavElement.UserMenuUniversity, onClick),
    className: cx(
      subMenuClassName,
      subMenuContainerBaseStyle,
      subMenuContainerThemeStyle[theme],
      {
        [subMenuActiveContainerStyle]: isUniversity,
      },
    ),
    description: (
      <UserMenuItemDescription
        theme={theme}
        isActive={isUniversity}
        link={hosts.university}
      />
    ),
  } as const;

  const getItemProps = (href: string | undefined): PolyProps => {
    if (href) {
      return { href, as: 'a' } as PolyProps;
    }

    return { as: 'button' } as PolyProps;
  };

  return (
    <>
      <UserMenuTrigger
        data-testid="user-menu-trigger"
        ref={triggerRef}
        open={open}
        onClick={toggleOpen}
        name={account?.firstName ?? ''}
        loading={loading}
      />
      <Menu
        open={open}
        setOpen={setOpen}
        usePortal={false}
        refEl={triggerRef}
        spacing={8}
        maxHeight={768}
        justify="end"
        className={cx(menuBaseStyle, menuThemeStyle[theme])}
      >
        {account?.shouldSeeAccountMfaBanner && (
          <li
            role="none"
            className={cx(mfaBannerBaseStyle, mfaBannerThemeStyle[theme])}
            data-testid="user-menu-mfa-banner"
          >
            <BellIcon size="small" />

            <Link
              className={linkColorStyle}
              href={`${hosts.account}/account/profile/security`}
            >
              MFA is now available for your Account!
            </Link>
          </li>
        )}

        <li
          role="none"
          className={cx(headerBaseStyle, headerThemeStyle[theme])}
        >
          <div className={cx(logoMarkBaseStyle, logoMarkThemeStyle[theme])}>
            <MongoDBLogoMark
              color={darkMode ? 'green-dark-2' : 'green-base'}
              height={30}
            />
          </div>

          <h3 className={cx(nameStyle, truncate)}>{name}</h3>

          <p className={cx(descriptionStyle, truncate)}>
            {account?.email ?? ''}
          </p>

          <FocusableMenuItem>
            <Button
              as={isAccount ? 'button' : 'a'}
              href={isAccount ? undefined : userMenuUrls.account.homepage}
              disabled={isAccount}
              data-testid="user-menu-account-button"
              darkMode={!darkMode}
            >
              Manage your MongoDB Account
            </Button>
          </FocusableMenuItem>
        </li>
        <SubMenu
          {...sharedProps}
          size="large"
          data-testid="user-submenu-cloud"
          active={isCloud}
          disabled={!account}
          href={hosts.cloud}
          title={isGovernment ? 'Atlas for Government' : 'Atlas'}
          glyph={
            <CloudIcon
              {...ariaHiddenProps}
              className={cx({ [menuGlyphThemeStyle[theme]]: !isCloud })}
            />
          }
          onClick={onElementClick(NavElement.UserMenuCloud, onClick)}
          description={
            <UserMenuItemDescription
              theme={theme}
              isActive={isCloud}
              product="cloud"
              isGovernment={isGovernment}
            />
          }
          className={cx(
            subMenuClassName,
            subMenuContainerBaseStyle,
            subMenuContainerThemeStyle[theme],
            {
              [subMenuActiveContainerStyle]: isCloud,
            },
          )}
        >
          <MenuItem
            {...getItemProps(cloudUrls.userPreferences)}
            active={activeNav === ActiveNavElement.UserMenuCloudUserPreferences}
            data-testid="user-menuitem-cloud-user-preferences"
            onClick={onElementClick(NavElement.UserMenuCloudUserPreferences)}
            className={cx(subMenuContainerThemeStyle[theme])}
          >
            User Preferences
          </MenuItem>

          <MenuItem
            {...getItemProps(cloudUrls.invitations)}
            active={activeNav === ActiveNavElement.UserMenuCloudInvitations}
            data-testid="user-menuitem-cloud-invitations"
            onClick={onElementClick(NavElement.UserMenuCloudInvitations)}
            className={cx(subMenuContainerThemeStyle[theme])}
          >
            <span className={subMenuItemStyle}>
              Invitations
              {(account?.openInvitations ?? 0) > 0 && (
                <Badge variant="darkgray">{account?.openInvitations}</Badge>
              )}
            </span>
          </MenuItem>

          <MenuItem
            {...getItemProps(cloudUrls.organizations)}
            active={activeNav === ActiveNavElement.UserMenuCloudOrganizations}
            data-testid="user-menuitem-cloud-organizations"
            onClick={onElementClick(NavElement.UserMenuCloudOrganizations)}
            className={cx(subMenuContainerThemeStyle[theme])}
          >
            Organizations
          </MenuItem>
          {!isGovernment && account?.hasLegacy2fa && (
            <MenuItem
              {...getItemProps(cloudUrls.mfa)}
              active={activeNav === ActiveNavElement.UserMenuCloudMFA}
              data-testid="user-menuitem-cloud-mfa"
              onClick={onElementClick(NavElement.UserMenuCloudMFA)}
              className={cx(subMenuContainerThemeStyle[theme])}
            >
              Legacy 2FA
            </MenuItem>
          )}
        </SubMenu>

        <MenuItem
          {...sharedProps}
          size="large"
          active={isDocs}
          href={userMenuUrls.docs}
          data-testid="user-menuitem-docs"
          glyph={
            <DocsIcon
              {...ariaHiddenProps}
              className={cx({ [menuGlyphThemeStyle[theme]]: !isDocs })}
            />
          }
          onClick={onElementClick(NavElement.UserMenuDocs, onClick)}
          className={cx(
            subMenuClassName,
            subMenuContainerBaseStyle,
            subMenuContainerThemeStyle[theme],
            {
              [subMenuActiveContainerStyle]: isDocs,
            },
          )}
          description={
            <UserMenuItemDescription
              theme={theme}
              isActive={isDocs}
              product="docs"
            />
          }
        >
          Documentation
        </MenuItem>

        {isUniversity ? (
          <SubMenu
            {...sharedProps}
            {...universitySharedProps}
            title={'University'}
          >
            <MenuItem
              {...getItemProps(userMenuUrls.university.transcript)}
              data-testid="user-menuitem-university-transcript"
              className={cx(subMenuContainerThemeStyle[theme])}
            >
              Transcript
            </MenuItem>
            <MenuItem
              {...getItemProps(userMenuUrls.university.faqs)}
              data-testid="user-menuitem-university-faqs"
              className={cx(subMenuContainerThemeStyle[theme])}
            >
              FAQ
            </MenuItem>
          </SubMenu>
        ) : (
          <MenuItem {...sharedProps} {...universitySharedProps}>
            University
          </MenuItem>
        )}

        <SubMenu
          {...sharedProps}
          size="large"
          active={isForums}
          href={hosts.forums}
          data-testid="user-submenu-forums"
          glyph={
            <ForumsIcon
              {...ariaHiddenProps}
              className={cx({ [menuGlyphThemeStyle[theme]]: !isForums })}
            />
          }
          title="Forums"
          onClick={onElementClick(NavElement.UserMenuForums, onClick)}
          className={cx(
            subMenuClassName,
            subMenuContainerBaseStyle,
            subMenuContainerThemeStyle[theme],
            {
              [subMenuActiveContainerStyle]: isForums,
            },
          )}
          description={
            <UserMenuItemDescription
              theme={theme}
              isActive={isForums}
              product="forums"
            />
          }
        >
          <MenuItem
            {...getItemProps(userMenuUrls.forums.forumsPreferences)}
            data-testid="user-menuitem-forum-preferences"
            className={cx(subMenuContainerThemeStyle[theme])}
          >
            Forum Preferences
          </MenuItem>
        </SubMenu>

        <MenuItem
          {...sharedProps}
          size="large"
          active={isDevHub}
          href={userMenuUrls.devHub}
          data-testid="user-menuitem-devhub"
          glyph={
            <DevHubIcon
              {...ariaHiddenProps}
              className={cx({ [menuGlyphThemeStyle[theme]]: !isDevHub })}
            />
          }
          onClick={onElementClick(NavElement.UserMenuDevHub, onClick)}
          className={cx(
            subMenuClassName,
            subMenuContainerBaseStyle,
            subMenuContainerThemeStyle[theme],
            {
              [subMenuActiveContainerStyle]: isDevHub,
            },
          )}
          description={
            <UserMenuItemDescription
              theme={theme}
              isActive={isDevHub}
              product="developer"
            />
          }
        >
          Developer Center
        </MenuItem>

        <SubMenu
          {...sharedProps}
          size="large"
          active={isSupport}
          disabled={!account}
          href={hosts.support}
          data-testid="user-submenu-support"
          title="Support"
          glyph={
            <SupportIcon
              {...ariaHiddenProps}
              className={cx({ [menuGlyphThemeStyle[theme]]: !isSupport })}
            />
          }
          onClick={onElementClick(NavElement.UserMenuSupport, onClick)}
          description={
            <UserMenuItemDescription
              theme={theme}
              isActive={isSupport}
              product="support"
              isGovernment={isGovernment}
            />
          }
          className={cx(
            subMenuClassName,
            subMenuContainerBaseStyle,
            subMenuContainerThemeStyle[theme],
            {
              [subMenuActiveContainerStyle]: isSupport,
            },
          )}
        >
          <MenuItem
            {...getItemProps(userMenuUrls.support.userPreferences)}
            data-testid="user-menuitem-support-user-preferences"
            className={cx(subMenuContainerThemeStyle[theme])}
          >
            User Preferences
          </MenuItem>
        </SubMenu>
        <MenuItem
          {...feedbackAnchorProps}
          size="large"
          glyph={
            <MegaphoneIcon
              {...ariaHiddenProps}
              className={menuGlyphThemeStyle[theme]}
            />
          }
          data-testid="user-menuitem-feedback"
          onClick={onElementClick(NavElement.UserMenuFeedback)}
          className={subMenuContainerThemeStyle[theme]}
        >
          {isGovernment ? 'Feature Requests' : 'Give us feedback'}
        </MenuItem>
        <MenuSeparator
          className={css`
            height: 1px;
          `}
        />
        <MenuItem
          onClick={onElementClick(NavElement.UserMenuLogout, onLogout)}
          {...getItemProps(userMenuUrls.logout)}
          size="large"
          data-testid="user-menuitem-logout"
          className={cx(
            css`
              margin: 6px 0;
            `,
            subMenuContainerThemeStyle[theme],
          )}
        >
          Log out
        </MenuItem>
      </Menu>
    </>
  );
}

UserMenu.displayName = 'UserMenu';

UserMenu.propTypes = {
  user: PropTypes.objectOf(PropTypes.string),
  activePlatform: PropTypes.oneOf([
    'account',
    'cloud',
    'support',
    'university',
    'devHub',
    'forums',
  ]),
  onLogout: PropTypes.func,
  onProductChange: PropTypes.func,
  onAccountClick: PropTypes.func,
};

export default UserMenu;
