/*
This file is part of the Notesnook project (https://notesnook.com/)

Copyright (C) 2022 Streetwriters (Private) Limited

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import { useCallback, useEffect } from "react";
import { Box, Button, Flex } from "@theme-ui/components";
import {
  Note,
  Notebook,
  StarOutline,
  Monographs,
  Tag,
  Trash,
  Settings,
  Notebook2,
  Tag2,
  Topic,
  DarkMode,
  LightMode,
  Login,
  Circle,
  Icon
} from "../icons";
import { AnimatedFlex } from "../animated";
import NavigationItem from "./navigation-item";
import { hardNavigate, navigate } from "../../navigation";
import { db } from "../../common/db";
import useMobile from "../../hooks/use-mobile";
import { showRenameColorDialog } from "../../common/dialog-controller";
import { useStore as useAppStore } from "../../stores/app-store";
import { useStore as useUserStore } from "../../stores/user-store";
import { useStore as useThemeStore } from "../../stores/theme-store";
import useLocation from "../../hooks/use-location";
import { FlexScrollContainer } from "../scroll-container";

type Route = {
  title: string;
  path: string;
  icon: Icon;
  isNew?: boolean;
};

const navigationHistory = new Map();
function shouldSelectNavItem(
  route: string,
  pin: { type: string; id: string; notebookId: string }
) {
  if (pin.type === "notebook") {
    return route === `/notebooks/${pin.id}`;
  } else if (pin.type === "topic") {
    return route === `/notebooks/${pin.notebookId}/${pin.id}`;
  } else if (pin.type === "tag") {
    return route === `/tags/${pin.id}`;
  }
  return false;
}

const routes: Route[] = [
  { title: "Notes", path: "/notes", icon: Note },
  {
    title: "Notebooks",
    path: "/notebooks",
    icon: Notebook
  },
  {
    title: "Favorites",
    path: "/favorites",
    icon: StarOutline
  },
  { title: "Tags", path: "/tags", icon: Tag },
  {
    title: "Monographs",
    path: "/monographs",
    icon: Monographs
  },
  { title: "Trash", path: "/trash", icon: Trash }
];

const settings: Route = {
  title: "Settings",
  path: "/settings",
  icon: Settings
};

type NavigationMenuProps = {
  toggleNavigationContainer: (toggleState?: boolean) => void;
  isTablet: boolean;
};

function NavigationMenu(props: NavigationMenuProps) {
  const { toggleNavigationContainer, isTablet } = props;
  const [location, previousLocation, state] = useLocation();
  const isFocusMode = useAppStore((store) => store.isFocusMode);
  const colors = useAppStore((store) => store.colors);
  const shortcuts = useAppStore((store) => store.shortcuts);
  const refreshNavItems = useAppStore((store) => store.refreshNavItems);
  const isLoggedIn = useUserStore((store) => store.isLoggedIn);
  const isMobile = useMobile();
  const theme = useThemeStore((store) => store.theme);
  const toggleNightMode = useThemeStore((store) => store.toggleNightMode);
  const setFollowSystemTheme = useThemeStore(
    (store) => store.setFollowSystemTheme
  );

  const _navigate = useCallback(
    (path) => {
      toggleNavigationContainer(true);
      const nestedRoute = findNestedRoute(path);
      navigate(!nestedRoute || nestedRoute === location ? path : nestedRoute);
    },
    [location, toggleNavigationContainer]
  );

  useEffect(() => {
    if (state === "forward" || state === "neutral")
      navigationHistory.set(location, true);
    else navigationHistory.delete(previousLocation);
  }, [location, previousLocation, state]);

  return (
    <AnimatedFlex
      id="navigation-menu"
      data-test-id="navigation-menu"
      initial={{
        opacity: 1
      }}
      animate={{
        opacity: isFocusMode ? 0 : 1,
        visibility: isFocusMode ? "collapse" : "visible"
      }}
      transition={{ duration: 0.3, ease: "easeOut" }}
      sx={{
        zIndex: 1,
        height: "auto",
        position: "relative",
        flex: 1,
        flexDirection: "column",
        justifyContent: "space-between"
      }}
      bg={"bgSecondary"}
      px={0}
    >
      <FlexScrollContainer>
        <Flex sx={{ flexDirection: "column" }}>
          {routes.map((item) => (
            <NavigationItem
              isTablet={isTablet}
              key={item.path}
              title={item.title}
              icon={item.icon}
              isNew={item.isNew}
              selected={
                item.path === "/"
                  ? location === item.path
                  : location.startsWith(item.path)
              }
              onClick={() => {
                if (!isMobile && location === item.path)
                  return toggleNavigationContainer();
                _navigate(item.path);
              }}
            />
          ))}
          {colors.map((color) => (
            <NavigationItem
              isTablet={isTablet}
              key={color.id}
              title={db.colors?.alias(color.id)}
              icon={Circle}
              selected={location === `/colors/${color.id}`}
              color={color.title.toLowerCase()}
              onClick={() => {
                _navigate(`/colors/${color.id}`);
              }}
              menuItems={[
                {
                  key: "rename",
                  title: () => "Rename color",
                  onClick: async () => {
                    await showRenameColorDialog(color.id);
                  }
                }
              ]}
            />
          ))}
          <Box
            bg="border"
            my={1}
            sx={{ width: "85%", height: "0.8px", alignSelf: "center" }}
          />
          {shortcuts.map((item) => (
            <NavigationItem
              isTablet={isTablet}
              key={item.id}
              title={item.type === "tag" ? db.tags?.alias(item.id) : item.title}
              menuItems={[
                {
                  key: "removeshortcut",
                  title: () => "Remove shortcut",
                  onClick: async () => {
                    await db.shortcuts?.remove(item.id);
                    refreshNavItems();
                  }
                }
              ]}
              icon={
                item.type === "notebook"
                  ? Notebook2
                  : item.type === "tag"
                  ? Tag2
                  : Topic
              }
              isShortcut
              selected={shouldSelectNavItem(location, item)}
              onClick={() => {
                if (item.type === "notebook") {
                  _navigate(`/notebooks/${item.id}`);
                } else if (item.type === "topic") {
                  _navigate(`/notebooks/${item.notebookId}/${item.id}`);
                } else if (item.type === "tag") {
                  _navigate(`/tags/${item.id}`);
                }
              }}
            />
          ))}
        </Flex>
      </FlexScrollContainer>
      <Flex sx={{ flexDirection: "column" }}>
        {!isLoggedIn && (
          <NavigationItem
            isTablet={isTablet}
            title="Login"
            icon={Login}
            onClick={() => hardNavigate("/login")}
          />
        )}

        <NavigationItem
          isTablet={isTablet}
          key={settings.path}
          title={settings.title}
          icon={settings.icon}
          onClick={() => {
            _navigate(settings.path);
          }}
          selected={location.startsWith(settings.path)}
        >
          {isTablet ? null : (
            <Button
              variant={"icon"}
              title="Toggle dark/light mode"
              sx={{
                bg: "transparent",
                borderRadius: "default",
                ":hover:not(disabled)": {
                  bg: "bgSecondaryHover",
                  filter: "brightness(100%)"
                }
              }}
              onClick={() => {
                setFollowSystemTheme(false);
                toggleNightMode();
              }}
            >
              {theme === "dark" ? (
                <DarkMode size={16} />
              ) : (
                <LightMode size={16} />
              )}
            </Button>
          )}
        </NavigationItem>
      </Flex>
    </AnimatedFlex>
  );
}
export default NavigationMenu;

function findNestedRoute(location: string) {
  let level = location.split("/").length;
  let nestedRoute = undefined;
  const history = Array.from(navigationHistory.keys());
  for (let i = history.length - 1; i >= 0; --i) {
    const route = history[i];
    if (!navigationHistory.get(route)) continue;

    const routeLevel = route.split("/").length;
    if (route.startsWith(location) && routeLevel > level) {
      level = routeLevel;
      nestedRoute = route;
    }
  }
  return nestedRoute;
}
