/*
 * QQQ - Low-code Application Framework for Engineers.
 * Copyright (C) 2021-2022.  Kingsrook, LLC
 * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
 * contact@kingsrook.com
 * https://github.com/Kingsrook/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

import {QBrandingMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QBrandingMetaData";
import Box from "@mui/material/Box";
import Divider from "@mui/material/Divider";
import Icon from "@mui/material/Icon";
import Link from "@mui/material/Link";
import List from "@mui/material/List";
import {ReactNode, useEffect, useReducer, useState} from "react";
import {NavLink, useLocation} from "react-router-dom";
import AuthenticationButton from "qqq/components/buttons/AuthenticationButton";
import SideNavCollapse from "qqq/components/horseshoe/sidenav/SideNavCollapse";
import SideNavItem from "qqq/components/horseshoe/sidenav/SideNavItem";
import SideNavList from "qqq/components/horseshoe/sidenav/SideNavList";
import SidenavRoot from "qqq/components/horseshoe/sidenav/SideNavRoot";
import sidenavLogoLabel from "qqq/components/horseshoe/sidenav/styles/SideNav";
import MDTypography from "qqq/components/legacy/MDTypography";
import {setMiniSidenav, setTransparentSidenav, setWhiteSidenav, useMaterialUIController,} from "qqq/context";


interface Props
{
   color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark";
   icon?: string;
   logo?: string;
   appName?: string;
   branding?: QBrandingMetaData;
   routes: {
      [key: string]:
         | ReactNode
         | string
         | {
         [key: string]:
            | ReactNode
            | string
            | {
            [key: string]:
               | ReactNode
               | string
               | {
               [key: string]: ReactNode | string;
            }[];
         }[];
      }[];
   }[];

   [key: string]: any;
}

function Sidenav({color, icon, logo, appName, branding, routes, ...rest}: Props): JSX.Element
{
   const [openCollapse, setOpenCollapse] = useState<boolean | string>(false);
   const [openNestedCollapse, setOpenNestedCollapse] = useState<boolean | string>(false);
   const [controller, dispatch] = useMaterialUIController();
   const {miniSidenav, transparentSidenav, whiteSidenav, darkMode} = controller;
   const location = useLocation();
   const {pathname} = location;
   const collapseName = pathname.split("/").slice(1)[0];
   const items = pathname.split("/").slice(1);
   const itemParentName = items[1];
   const itemName = items[items.length - 1];
   const [, forceUpdate] = useReducer((x) => x + 1, 0);

   let textColor:
      | "primary"
      | "secondary"
      | "info"
      | "success"
      | "warning"
      | "error"
      | "dark"
      | "white"
      | "inherit"
      | "text"
      | "light" = "white";

   if (transparentSidenav || (whiteSidenav && !darkMode))
   {
      textColor = "dark";
   }
   else if (whiteSidenav && darkMode)
   {
      textColor = "inherit";
   }

   const closeSidenav = () => setMiniSidenav(dispatch, true);

   useEffect(() =>
   {
      setOpenCollapse(collapseName);
      setOpenNestedCollapse(itemParentName);
   }, []);

   useEffect(() =>
   {
      // A function that sets the mini state of the sidenav.
      function handleMiniSidenav()
      {
         setMiniSidenav(dispatch, window.innerWidth < 1200);
         setTransparentSidenav(dispatch, window.innerWidth < 1200 ? false : transparentSidenav);
         setWhiteSidenav(dispatch, window.innerWidth < 1200 ? false : whiteSidenav);
      }

      /**
       The event listener that's calling the handleMiniSidenav function when resizing the window.
       */
      window.addEventListener("resize", handleMiniSidenav);
      window.onload = () =>
      {
         forceUpdate();
      };

      // Call the handleMiniSidenav function to set the state with the initial value.
      handleMiniSidenav();

      // Remove event listener on cleanup
      return () => window.removeEventListener("resize", handleMiniSidenav);
   }, [dispatch, location]);

   // Render all the nested collapse items from the routes.js
   const renderNestedCollapse = (collapse: any) =>
   {
      const template = collapse.map(({name, route, key, href}: any) =>
         href ? (
            <Link
               key={key}
               href={href}
               target="_blank"
               rel="noreferrer"
               sx={{textDecoration: "none"}}
            >
               <SideNavItem name={name} nested />
            </Link>
         ) : (
            <NavLink to={route} key={key} style={{textDecoration: "none"}}>
               <SideNavItem name={name} active={route === pathname} nested />
            </NavLink>
         )
      );

      return template;
   };
   // Render the all the collpases from the routes.js
   const renderCollapse = (collapses: any) =>
      collapses.map(({name, collapse, route, href, key}: any) =>
      {
         let returnValue;

         if (collapse)
         {
            returnValue = (
               <SideNavItem
                  key={key}
                  color={color}
                  name={name}
                  active={key === itemParentName ? "isParent" : false}
                  open={openNestedCollapse === key}
                  onClick={({currentTarget}: any) =>
                     openNestedCollapse === key && currentTarget.classList.contains("MuiListItem-root")
                        ? setOpenNestedCollapse(false)
                        : setOpenNestedCollapse(key)
                  }
               >
                  {renderNestedCollapse(collapse)}
               </SideNavItem>
            );
         }
         else
         {
            returnValue = href ? (
               <Link
                  href={href}
                  key={key}
                  target="_blank"
                  rel="noreferrer"
                  sx={{textDecoration: "none"}}
               >
                  <SideNavItem color={color} name={name} active={key === itemName} />
               </Link>
            ) : (
               <NavLink to={route} key={key} style={{textDecoration: "none"}}>
                  <SideNavItem color={color} name={name} active={key === itemName} />
               </NavLink>
            );
         }
         return <SideNavList key={key}>{returnValue}</SideNavList>;
      });

   // Render all the routes from the routes.js (All the visible items on the Sidenav)
   const renderRoutes = routes.map(
      ({type, name, icon, title, collapse, noCollapse, key, href, route}: any) =>
      {
         let returnValue;

         if (type === "collapse")
         {
            if (href)
            {
               returnValue = (
                  <Link
                     href={href}
                     key={key}
                     target="_blank"
                     rel="noreferrer"
                     sx={{textDecoration: "none"}}
                  >
                     <SideNavCollapse
                        name={name}
                        icon={icon}
                        active={key === collapseName}
                        noCollapse={noCollapse}
                     />
                  </Link>
               );
            }
            else if (noCollapse && route)
            {
               returnValue = (
                  <NavLink to={route} key={key}>
                     <SideNavCollapse
                        name={name}
                        icon={icon}
                        noCollapse={noCollapse}
                        active={key === collapseName}
                     >
                        {collapse ? renderCollapse(collapse) : null}
                     </SideNavCollapse>
                  </NavLink>
               );
            }
            else
            {
               returnValue = (
                  <SideNavCollapse
                     key={key}
                     name={name}
                     icon={icon}
                     active={key === collapseName}
                     open={openCollapse === key}
                     noCollapse={noCollapse}
                     onClick={() => (! noCollapse ? (openCollapse === key ? setOpenCollapse(false) : setOpenCollapse(key)) : null) }
                  >
                     {collapse ? renderCollapse(collapse) : null}
                  </SideNavCollapse>
               );
            }
         }
         else if (type === "title")
         {
            returnValue = (
               <MDTypography
                  key={key}
                  color={textColor}
                  display="block"
                  variant="caption"
                  fontWeight="bold"
                  textTransform="uppercase"
                  pl={3}
                  mt={2}
                  mb={1}
                  ml={1}
               >
                  {title}
               </MDTypography>
            );
         }
         else if (type === "divider")
         {
            returnValue = (
               <Divider
                  key={key}
                  light={
                     (!darkMode && !whiteSidenav && !transparentSidenav) ||
                     (darkMode && !transparentSidenav && whiteSidenav)
                  }
               />
            );
         }

         return returnValue;
      }
   );

   return (
      <SidenavRoot
         {...rest}
         variant="permanent"
         ownerState={{transparentSidenav, whiteSidenav, miniSidenav, darkMode}}
      >
         <Box pt={3} mr={1} pb={0} px={4} textAlign="center">
            <Box
               display={{xs: "block", xl: "none"}}
               position="absolute"
               top={0}
               right={0}
               p={1.625}
               onClick={closeSidenav}
               sx={{cursor: "pointer"}}
            >
               <MDTypography variant="h6" color="secondary">
                  <Icon sx={{fontWeight: "bold"}}>close</Icon>
               </MDTypography>
            </Box>
            <Box component={NavLink} to="/" display="flex" alignItems="center">
               {!miniSidenav && logo && <Box component="img" src={logo} alt={appName} title={appName} width="100%" />}
               {miniSidenav && icon && <Box component="img" src={icon} alt={appName} title={appName} width="160%" />}
               {!miniSidenav && !logo && appName && <Box width={appName && "100%"} sx={(theme: any) => sidenavLogoLabel(theme, {miniSidenav})}>
                  <MDTypography component="h6" variant="button" fontWeight="medium" color={textColor}>
                     {appName}
                  </MDTypography>
               </Box>
               }
            </Box>
            {
               branding && branding.environmentBannerText &&
               <Box mt={2} bgcolor={branding.environmentBannerColor} borderRadius={2}>
                  {branding.environmentBannerText}
               </Box>
            }
         </Box>
         <Divider
            light={
               (!darkMode && !whiteSidenav && !transparentSidenav) ||
               (darkMode && !transparentSidenav && whiteSidenav)
            }
         />
         <List>{renderRoutes}</List>
         <Divider
            light={
               (!darkMode && !whiteSidenav && !transparentSidenav) ||
               (darkMode && !transparentSidenav && whiteSidenav)
            }
         />
         <AuthenticationButton />
      </SidenavRoot>
   );
}

// Declaring default props for Sidenav
Sidenav.defaultProps = {
   color: "info",
   icon: "",
   logo: "",
   appName: "",
};

export default Sidenav;
