import React, {
  useState,
  createContext,
  useContext,
  useMemo,
  forwardRef,
} from "react";
import classNames from "classnames";
import { createRocket, RocketProps } from "../_util/create-rocket";
import { InferProps } from "../_type";
import { LayoutContent } from "./LayoutContent";
import { isChildOfType } from "../_util/is-child-of-type";
import { useConfig } from "../_util/config-context";
import { uuid } from "../_util/uuid";
import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect";
import { noop } from "../_util/noop";

const LayoutHeader = createRocket(
  "LayoutHeader",
  "header.@{prefix}-layout__header"
);
const LayoutFooter = createRocket(
  "LayoutFooter",
  "footer.@{prefix}-layout__footer"
);

interface LayoutBodyContextValue {
  setSider?: (id: string, value: boolean) => void;
}

const LayoutBodyContext = createContext<LayoutBodyContextValue>({
  setSider: noop,
});

export const LayoutSider = forwardRef(function LayoutSider(
  { className, ...props }: RocketProps,
  ref: React.Ref<HTMLElement>
) {
  const { classPrefix } = useConfig();
  const { setSider } = useContext(LayoutBodyContext);

  useIsomorphicLayoutEffect(() => {
    const id = uuid();
    setSider(id, true);
    return () => setSider(id, false);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <nav
      ref={ref}
      className={classNames(`${classPrefix}-layout__sidebar`, className)}
      {...props}
    />
  );
});

LayoutSider.displayName = "LayoutSider";

export const LayoutBody = forwardRef(function LayoutBody(
  { className, children, ...props }: RocketProps,
  ref: React.Ref<HTMLElement>
) {
  const { classPrefix } = useConfig();
  const [siders, setSiders] = useState<string[]>([]);

  const hasSider = useMemo(() => {
    let hasSider = false;
    React.Children.forEach(children, child => {
      if (isChildOfType(child, LayoutSider)) {
        hasSider = true;
      }
    });
    return hasSider;
  }, [children]);

  return (
    <LayoutBodyContext.Provider
      value={{
        setSider: (id, value) => {
          if (value) {
            setSiders(siders => [...siders, id]);
          } else {
            setSiders(siders => siders.filter(i => i !== id));
          }
        },
      }}
    >
      <section
        ref={ref}
        className={classNames(
          `${classPrefix}-layout__body`,
          { "has-subsidebar": hasSider || siders.length > 0 },
          className
        )}
        {...props}
      >
        {children}
      </section>
    </LayoutBodyContext.Provider>
  );
});

LayoutBody.displayName = "LayoutBody";

export const Layout = Object.assign(
  createRocket("Layout", "section.@{prefix}-layout"),
  {
    Header: LayoutHeader,
    Body: LayoutBody,
    Footer: LayoutFooter,
    Sider: LayoutSider,
    Content: LayoutContent,
  }
);

export interface LayoutProps extends InferProps<typeof Layout> {}
