import Link from "next/link";
import { useRouter } from "next/router";
import React, { useReducer } from "react";

import { Image } from "./Image";

import styles from "./Navbar.module.css";

type Menu = {
  name: string;
  route: string;
  exact?: boolean;
  submenu?: Menu;
}[];

const menu: Menu = [
  {
    name: "Home",
    route: "/",
    exact: true,
  },
  {
    name: "About",
    route: "/about",
    submenu: [
      {
        name: "About Us",
        route: "/about",
        exact: true,
      },
      {
        name: "Meet the Team",
        route: "/about/team",
      },
      {
        name: "Members",
        route: "/about/members",
      },
      {
        name: "Constitution",
        route: "/about/constitution",
      },
      {
        name: "Code of Conduct",
        route: "/about/code-of-conduct",
      },
      {
        name: "Our Supporters",
        route: "/about/our-supporters",
      },
    ],
  },
  {
    name: "Get Involved",
    route: "/get-involved",
  },
  {
    name: "Events",
    route: "/events",
  },
  {
    name: "Resources",
    route: "/resources/services",
    submenu: [
      {
        name: "Services",
        route: "/resources/services",
      },
      {
        name: "Machine Usage",
        route: "/resources/machine-usage-agreement",
      },
      {
        name: "Tech Talks",
        route: "/resources/tech-talks",
      },
      {
        name: "CS Club Wiki",
        route: "https://wiki.csclub.uwaterloo.ca",
      },
      {
        name: "Advice",
        route: "/resources/advice/co-op",
        submenu: [
          {
            name: "Co-op Advice",
            route: "/resources/advice/co-op",
          },
          {
            name: "Academic Advice",
            route: "/resources/advice/academic",
          },
          {
            name: "Additional Resources",
            route: "/resources/advice/misc",
          },
        ],
      },
      {
        name: "Internships",
        route: "https://github.com/uwcsc/2023-internships",
      },
    ],
  },
];

export function Navbar() {
  const router = useRouter();
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <nav className={styles.navbar}>
      <div className={styles.navContent}>
        <Link href="/">
          <a className={styles.logo}>
            <Image src="/images/logo-icon.svg" alt="CSC Logo" />
          </a>
        </Link>
        <button
          className={styles.hamburger}
          onClick={() => dispatch({ type: "open", route: router.pathname })}
        >
          <HamburgerSvg />
        </button>
        <div
          className={
            state.isNavOpen
              ? `${styles.navMobileBackground} ${styles.show}`
              : styles.navMobileBackground
          }
          onClick={() => dispatch({ type: "close" })}
        />
        <div className={styles.navMenuWrapper}>
          <Link href="/">
            <a
              className={styles.logoMobile}
              onClick={() => dispatch({ type: "close" })}
            >
              <Image src="/images/logo-icon.svg" alt="CSC Logo" />
            </a>
          </Link>
          <ul className={styles.navMenu}>
            {menu.map((item) => {
              return (
                <li className={styles.itemWrapper} key={item.name}>
                  <NavItem
                    name={item.name}
                    route={item.route}
                    submenu={item.submenu}
                    mainRouteActive={state.activeSubmenus.has(
                      getMainRoute(item.route)
                    )}
                    onClose={() => dispatch({ type: "close" })}
                    onToggle={(route) => dispatch({ type: "toggle", route })}
                  />
                </li>
              );
            })}
          </ul>
        </div>
      </div>
    </nav>
  );
}

interface MobileState {
  isNavOpen: boolean;
  activeSubmenus: Set<string>; // strings are NavLink routes
}

type MobileAction =
  | { type: "open"; route: string }
  | { type: "toggle"; route: string }
  | { type: "close" };

const initialState: MobileState = {
  isNavOpen: false,
  activeSubmenus: new Set(),
};

function reducer(state: MobileState, action: MobileAction): MobileState {
  switch (action.type) {
    case "open":
      return {
        isNavOpen: true,
        activeSubmenus: new Set([getMainRoute(action.route)]),
      };
    case "toggle": {
      const newSet = new Set(state.activeSubmenus);
      if (state.activeSubmenus.has(getMainRoute(action.route))) {
        newSet.delete(getMainRoute(action.route));
      } else {
        newSet.add(getMainRoute(action.route));
      }
      return {
        isNavOpen: state.isNavOpen,
        activeSubmenus: newSet,
      };
    }
    case "close":
      return initialState;
  }
}

interface NavItemProps {
  name: string;
  route: string;
  submenu?: {
    name: string;
    route: string;
  }[];
  mainRouteActive: boolean;
  onToggle(route: string): void;
  onClose(): void;
}

function NavItem(props: NavItemProps) {
  const router = useRouter();
  const isCurrentPage = shouldHighlight(
    router.pathname,
    props.name,
    props.route
  );
  const isExternalLink =
    props.route.includes("http://") || props.route.includes("https://");

  function handleClick() {
    if (document.activeElement instanceof HTMLElement) {
      document.activeElement.blur();
    }
    props.onClose();
  }

  return (
    <>
      {isExternalLink ? (
        <a
          href={props.route}
          target="_blank"
          rel="noopener noreferrer"
          onClick={handleClick}
        >
          {props.name}
        </a>
      ) : (
        <Link href={props.route}>
          <a
            title={props.name}
            className={isCurrentPage ? styles.currentPage : ""}
            onClick={handleClick}
          >
            {props.name}
          </a>
        </Link>
      )}
      {(props.submenu?.length ?? 0) > 0 ? (
        <>
          <button
            className={
              props.mainRouteActive
                ? `${styles.dropdownIcon} ${styles.rotate}`
                : styles.dropdownIcon
            }
            onClick={() => props.onToggle(props.route)}
          >
            <DropdownSvg />
          </button>
          <ul
            className={
              props.mainRouteActive
                ? `${styles.dropdown} ${styles.show}`
                : styles.dropdown
            }
          >
            {props.submenu?.map((item) => {
              return (
                <li className={styles.itemWrapper} key={item.name}>
                  <NavItem
                    name={item.name}
                    route={item.route}
                    mainRouteActive={props.mainRouteActive}
                    onClose={() => props.onClose()}
                    onToggle={(route) => props.onToggle(route)}
                  />
                </li>
              );
            })}
          </ul>
        </>
      ) : null}
    </>
  );
}

interface Leaf {
  name: string;
  route: string;
  exact?: boolean;
  ancestors: { name: string; route: string }[];
}

function collectLeaves(
  accumulator: Leaf[],
  entry: {
    name: string;
    route: string;
    exact?: boolean;
    submenu?: Menu;
  }
): Leaf[] {
  if (entry.submenu == null) {
    return [...accumulator, { ...entry, ancestors: [] }];
  }

  const subleaves = entry.submenu.reduce(collectLeaves, [] as Leaf[]);
  return [
    ...accumulator,
    ...subleaves.map((leaf) => ({
      ...leaf,
      ancestors: [...leaf.ancestors, { name: entry.name, route: entry.route }],
    })),
  ];
}

const leaves: Leaf[] = menu.reduce(collectLeaves, [] as Leaf[]);

function shouldHighlight(
  pathname: string,
  name: string,
  route: string
): boolean {
  const match = leaves.find((leaf) =>
    leaf.exact ? leaf.route === pathname : pathname.startsWith(leaf.route)
  );
  return match
    ? (match.name === name && match.route === route) ||
        match.ancestors.find(
          (ancestor) => ancestor.name === name && ancestor.route === route
        ) != null
    : false;
}

function getMainRoute(route: string) {
  if (route === "/") {
    return "/";
  } else if (route.startsWith("http://") || route.startsWith("https://")) {
    return route;
  }
  return "/" + route.split("/")[1];
}

function HamburgerSvg() {
  return (
    <svg
      width="30"
      height="23"
      viewBox="0 0 30 23"
      className={styles.icon}
      xmlns="http://www.w3.org/2000/svg"
    >
      <line
        x1="28"
        y1="2"
        x2="2"
        y2="2"
        stroke="#2A2A62"
        strokeWidth="4"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <line
        x1="28"
        y1="11.375"
        x2="2"
        y2="11.375"
        stroke="#2A2A62"
        strokeWidth="4"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <line
        x1="28"
        y1="20.75"
        x2="2"
        y2="20.75"
        stroke="#2A2A62"
        strokeWidth="4"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function DropdownSvg() {
  return (
    <svg
      width="14"
      height="9"
      viewBox="0 0 14 9"
      fill="none"
      className={styles.icon}
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M7.75593 8.12713C7.35716 8.58759 6.64284 8.58759 6.24407 8.12713L0.638743 1.65465C0.0778675 1.00701 0.537921 0 1.39467 0L12.6053 0C13.4621 0 13.9221 1.00701 13.3613 1.65465L7.75593 8.12713Z"
        fill="#2A2A62"
      />
    </svg>
  );
}
