import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { FunctionComponent, useContext, useMemo } from "react";
import { Badge, Collapse, ListGroupItem } from "react-bootstrap";
import { NavLink } from "react-router-dom";
import { siteChangeSidebar } from "../@redux/actions";
import { useReduxAction, useReduxStore } from "../@redux/hooks/base";
import { SidebarToggleContext } from "../App";
import {
  BadgeProvider,
  ChildBadgeProvider,
  CollapseItemType,
  LinkItemType,
} from "./types";

export const HiddenKeysContext = React.createContext<string[]>([]);

export const BadgesContext = React.createContext<BadgeProvider>({});

function useToggleSidebar() {
  return useReduxAction(siteChangeSidebar);
}

function useSidebarKey() {
  return useReduxStore((s) => s.site.sidebar);
}

export const LinkItem: FunctionComponent<LinkItemType> = ({
  link,
  name,
  icon,
}) => {
  const badges = useContext(BadgesContext);
  const toggle = useContext(SidebarToggleContext);

  const badgeValue = useMemo(() => {
    let badge: Nullable<number> = null;
    if (name in badges) {
      let item = badges[name];
      if (typeof item === "number") {
        badge = item;
      }
    }
    return badge;
  }, [badges, name]);

  return (
    <NavLink
      activeClassName="sb-active"
      className="list-group-item list-group-item-action sidebar-button"
      to={link}
      onClick={toggle}
    >
      <DisplayItem
        badge={badgeValue ?? undefined}
        name={name}
        icon={icon}
      ></DisplayItem>
    </NavLink>
  );
};

export const CollapseItem: FunctionComponent<CollapseItemType> = ({
  icon,
  name,
  children,
}) => {
  const badges = useContext(BadgesContext);
  const hiddenKeys = useContext(HiddenKeysContext);
  const toggleSidebar = useContext(SidebarToggleContext);

  const sidebarKey = useSidebarKey();
  const updateSidebar = useToggleSidebar();

  const [badgeValue, childValue] = useMemo<
    [Nullable<number>, Nullable<ChildBadgeProvider>]
  >(() => {
    let badge: Nullable<number> = null;
    let child: Nullable<ChildBadgeProvider> = null;

    if (name in badges) {
      const item = badges[name];
      if (typeof item === "number") {
        badge = item;
      } else if (typeof item === "object") {
        badge = 0;
        child = item;
        for (const it in item) {
          badge += item[it];
        }
      }
    }
    return [badge, child];
  }, [badges, name]);

  const active = useMemo(() => sidebarKey === name, [sidebarKey, name]);

  const collapseBoxClass = useMemo(
    () => `sidebar-collapse-box ${active ? "active" : ""}`,
    [active]
  );

  const childrenElems = useMemo(
    () =>
      children
        .filter((v) => !hiddenKeys.includes(v.hiddenKey ?? ""))
        .map((ch) => {
          let badge: Nullable<number> = null;
          if (childValue && ch.name in childValue) {
            badge = childValue[ch.name];
          }
          return (
            <NavLink
              key={ch.name}
              activeClassName="sb-active"
              className="list-group-item list-group-item-action sidebar-button sb-collapse"
              to={ch.link}
              onClick={toggleSidebar}
            >
              <DisplayItem
                badge={badge === 0 ? undefined : badge ?? undefined}
                name={ch.name}
              ></DisplayItem>
            </NavLink>
          );
        }),
    [children, hiddenKeys, childValue, toggleSidebar]
  );

  if (childrenElems.length === 0) {
    return null;
  }

  return (
    <div className={collapseBoxClass}>
      <ListGroupItem
        action
        className="sidebar-button"
        onClick={() => {
          if (active) {
            updateSidebar("");
          } else {
            updateSidebar(name);
          }
        }}
      >
        <DisplayItem
          badge={badgeValue === 0 ? undefined : badgeValue ?? undefined}
          icon={icon}
          name={name}
        ></DisplayItem>
      </ListGroupItem>
      <Collapse in={active}>
        <div className="sidebar-collapse">{childrenElems}</div>
      </Collapse>
    </div>
  );
};

interface DisplayProps {
  name: string;
  icon?: IconDefinition;
  badge?: number;
}

const DisplayItem: FunctionComponent<DisplayProps> = ({
  name,
  icon,
  badge,
}) => (
  <React.Fragment>
    {icon && (
      <FontAwesomeIcon size="1x" className="icon" icon={icon}></FontAwesomeIcon>
    )}
    <span className="d-flex flex-grow-1 justify-content-between">
      {name} <Badge variant="secondary">{badge}</Badge>
    </span>
  </React.Fragment>
);
