/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React, { useCallback, useState, useEffect } from "react";
import clsx from "clsx";
import Link from "@docusaurus/Link";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import useBaseUrl from "@docusaurus/useBaseUrl";

import SearchBar from "../SearchBar";
import useThemeContext from "@theme/hooks/useThemeContext";
import useHideableNavbar from "@theme/hooks/useHideableNavbar";
import useLockBodyScroll from "@theme/hooks/useLockBodyScroll";
import useWindowSize, { windowSizes } from "@theme/hooks/useWindowSize";
import useLogo from "@theme/hooks/useLogo";
import useMedia from "use-media";
import { ColorModeToggle } from "../../components/ColorModeToggle";
import { GithubButton } from "../../components/GithubButton";
import Logo from "../../components/Logo";
import styles from "./styles.module.css";

// retrocompatible with v1
const DefaultNavItemPosition = "right";

function NavLink({
  activeBasePath,
  activeBaseRegex,
  to,
  href,
  label,
  activeClassName = "navbar__link--active",
  prependBaseUrlToHref,
  ...props
}) {
  const toUrl = useBaseUrl(to);
  const activeBaseUrl = useBaseUrl(activeBasePath);
  const normalizedHref = useBaseUrl(href, { forcePrependBaseUrl: true });

  return (
    <Link
      {...(href
        ? {
            target: "_blank",
            rel: "noopener noreferrer",
            href: prependBaseUrlToHref ? normalizedHref : href,
          }
        : {
            isNavLink: true,
            activeClassName,
            to: toUrl,
            ...(activeBasePath || activeBaseRegex
              ? {
                  isActive: (_match, location) =>
                    activeBaseRegex
                      ? new RegExp(activeBaseRegex).test(location.pathname)
                      : location.pathname.startsWith(activeBaseUrl),
                }
              : null),
          })}
      {...props}
    >
      {label}
    </Link>
  );
}

function NavItem({
  items,
  position = DefaultNavItemPosition,
  className,
  ...props
}) {
  const navLinkClassNames = (extraClassName, isDropdownItem = false) =>
    clsx(
      styles.navLink,
      {
        "navbar__item navbar__link": !isDropdownItem,
        dropdown__link: isDropdownItem,
      },
      extraClassName
    );

  if (!items) {
    return <NavLink className={navLinkClassNames(className)} {...props} />;
  }

  return (
    <div
      className={clsx("navbar__item", "dropdown", "dropdown--hoverable", {
        "dropdown--left": position === "left",
        "dropdown--right": position === "right",
      })}
    >
      <NavLink
        className={navLinkClassNames(className)}
        {...props}
        onClick={(e) => e.preventDefault()}
        onKeyDown={(e) => {
          if (e.key === "Enter") {
            e.target.parentNode.classList.toggle("dropdown--show");
          }
        }}
      >
        {props.label}
      </NavLink>
      <ul className="dropdown__menu">
        {items.map(
          ({ className: childItemClassName, ...childItemProps }, i) => (
            <li key={i}>
              <NavLink
                activeClassName="dropdown__link--active"
                className={navLinkClassNames(childItemClassName, true)}
                {...childItemProps}
              />
            </li>
          )
        )}
      </ul>
    </div>
  );
}

function MobileNavItem({ items, position: _position, className, ...props }) {
  // Need to destructure position from props so that it doesn't get passed on.
  const navLinkClassNames = (extraClassName, isSubList = false) =>
    clsx(
      "menu__link",
      {
        "menu__link--sublist": isSubList,
      },
      extraClassName
    );

  if (!items) {
    return (
      <li className="menu__list-item">
        <NavLink className={navLinkClassNames(className)} {...props} />
      </li>
    );
  }

  return (
    <li className="menu__list-item">
      <NavLink className={navLinkClassNames(className, true)} {...props}>
        {props.label}
      </NavLink>
      <ul className="menu__list">
        {items.map(
          ({ className: childItemClassName, ...childItemProps }, i) => (
            <li className="menu__list-item" key={i}>
              <NavLink
                activeClassName="menu__link--active"
                className={navLinkClassNames(childItemClassName)}
                {...childItemProps}
                onClick={props.onClick}
              />
            </li>
          )
        )}
      </ul>
    </li>
  );
}

// If split links by left/right
// if position is unspecified, fallback to right (as v1)
function splitLinks(links) {
  const leftLinks = links.filter(
    (linkItem) => (linkItem.position ?? DefaultNavItemPosition) === "left"
  );
  const rightLinks = links.filter(
    (linkItem) => (linkItem.position ?? DefaultNavItemPosition) === "right"
  );
  return {
    leftLinks,
    rightLinks,
  };
}

function Navbar() {
  const {
    siteConfig: {
      themeConfig: {
        navbar: { title, links = [], hideOnScroll = false } = {},
        disableDarkMode = false,
      },
      organizationName,
      projectName,
      baseUrl,
    },
  } = useDocusaurusContext();
  const [sidebarShown, setSidebarShown] = useState(false);
  const [isSearchBarExpanded, setIsSearchBarExpanded] = useState(false);

  const { isDarkTheme, setLightTheme, setDarkTheme } = useThemeContext();
  const { navbarRef, isNavbarVisible } = useHideableNavbar(hideOnScroll);
  const { logoLink, logoLinkProps, logoImageUrl, logoAlt } = useLogo();

  useLockBodyScroll(sidebarShown);

  const showSidebar = useCallback(() => {
    setSidebarShown(true);
  }, [setSidebarShown]);
  const hideSidebar = useCallback(() => {
    setSidebarShown(false);
  }, [setSidebarShown]);

  const onToggleChange = useCallback(
    (checked) => {
      checked ? setDarkTheme() : setLightTheme();
    },
    [setLightTheme, setDarkTheme]
  );

  const windowSize = useWindowSize();

  const isMobile = useMedia({ maxWidth: 997 });

  useEffect(() => {
    if (windowSize === windowSizes.desktop) {
      setSidebarShown(false);
    }
  }, [windowSize]);

  const { leftLinks, rightLinks } = splitLinks(links);

  return (
    <nav
      ref={navbarRef}
      className={clsx("navbar", "navbar--light", "navbar--fixed-top", {
        "navbar-sidebar--show": sidebarShown,
        [styles.navbarHideable]: hideOnScroll,
        [styles.navbarHidden]: !isNavbarVisible,
        [styles.navBarBorder]: !isMobile,
      })}
    >
      <div className="navbar__inner">
        <div className="navbar__items">
          {!isMobile && (
            <Link
              className="navbar__brand"
              to={baseUrl}
              aria-label="Home"
              title="Home"
            >
              <Logo size={32} className={styles.logo} />
              {title != null && (
                <strong
                  className={isSearchBarExpanded ? styles.hideLogoText : ""}
                >
                  {title}
                </strong>
              )}
            </Link>
          )}
          {links != null && links.length !== 0 && (
            <div
              aria-label="Navigation bar toggle"
              className="navbar__toggle"
              role="button"
              tabIndex={0}
              onClick={showSidebar}
              onKeyDown={showSidebar}
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="30"
                height="30"
                viewBox="0 0 30 30"
                role="img"
                focusable="false"
              >
                <title>Menu</title>
                <path
                  stroke="currentColor"
                  strokeLinecap="round"
                  strokeMiterlimit="10"
                  strokeWidth="2"
                  d="M4 7h22M4 15h22M4 23h22"
                />
              </svg>
            </div>
          )}

          {leftLinks.map((linkItem, i) => (
            <NavItem {...linkItem} key={i} />
          ))}
        </div>
        <div className="navbar__items navbar__items--right">
          {isMobile && (
            <Link
              className="navbar__brand"
              to={baseUrl}
              aria-label="Home"
              title="Home"
            >
              <Logo size={32} className={styles.logo} />
              {title != null && (
                <strong
                  className={isSearchBarExpanded ? styles.hideLogoText : ""}
                >
                  {title}
                </strong>
              )}
            </Link>
          )}

          {!isMobile && (
            <SearchBar
              handleSearchBarToggle={setIsSearchBarExpanded}
              isSearchBarExpanded={isSearchBarExpanded}
              style={{ marginLeft: "0.5rem", marginRight: "0.5rem" }}
            />
          )}

          {!disableDarkMode && !isMobile && (
            <ColorModeToggle
              toggle={onToggleChange}
              isDark={isDarkTheme}
              style={{ marginLeft: "0.5rem", marginRight: "0.5rem" }}
            />
          )}
          {!isMobile && (
            <GithubButton
              href={`https://github.com/${organizationName}/${projectName}`}
            />
          )}
        </div>
      </div>
      <div
        role="presentation"
        className="navbar-sidebar__backdrop"
        onClick={hideSidebar}
      />
      <div className="navbar-sidebar">
        <div className="navbar-sidebar__brand">
          <Link
            className="navbar__brand"
            onClick={hideSidebar}
            to={logoLink}
            {...logoLinkProps}
          >
            <Logo size={32} className={styles.logo} />
            {title != null && (
              <strong
                className={
                  isSearchBarExpanded ? styles.hideLogoText : "navbar__title"
                }
              >
                {title}
              </strong>
            )}
          </Link>
          {sidebarShown && (
            <GithubButton
              href={`https://github.com/${organizationName}/${projectName}`}
            />
          )}
          {!disableDarkMode && sidebarShown && (
            <ColorModeToggle toggle={onToggleChange} isDark={isDarkTheme} />
          )}
        </div>
        <div className="navbar-sidebar__items">
          <div className="menu">
            <ul className="menu__list">
              {links.map((linkItem, i) => (
                <MobileNavItem {...linkItem} onClick={hideSidebar} key={i} />
              ))}
              <div style={{ margin: 5, marginTop: 15 }} />
              <SearchBar
                handleSearchBarToggle={setIsSearchBarExpanded}
                isSearchBarExpanded={isSearchBarExpanded}
              />
            </ul>
          </div>
        </div>
      </div>
    </nav>
  );
}

export default Navbar;
