import { TextUtil } from "@greatness/util";
import { useCallback, useEffect, useState } from "react";

import ComponentErrorBoundary from "./ComponentErrorBoundary";
import createUseStyles from "./theme/createUseStyles";
import { cn } from "./util/cn";

const useStyles = createUseStyles({
  collapsible: {
    "& h2, & h3, & h4": {
      margin: 0,
    },
    appearance: "none",
  },
});

class CollapsibleStorage {
  static set(blockKey: string, isCollapsed: boolean) {
    const key = CollapsibleStorage.getKey(blockKey);
    const value = isCollapsed ? "true" : "false";
    localStorage.setItem(key, value);
  }

  static isCollapsed(blockKey: string) {
    const storageValue = localStorage.getItem(CollapsibleStorage.getKey(blockKey));
    if (storageValue === null) {
      return true;
    }
    return storageValue === "true";
  }

  static getKey(blockKey: string) {
    return `is-collapsed-${blockKey}`;
  }
}

const useCollapsible = ({
  blockKey: blockKeyProp,
  title,
}: {
  blockKey?: string;
  title: string | React.ReactNode;
}) => {
  const blockKey = (() => {
    if (!TextUtil.isEmpty(blockKeyProp)) {
      return blockKeyProp!;
    }
    if (typeof title === "string") {
      return title;
    }
    throw new Error("Invalid block key");
  })();

  const [isCollapsed, setIsCollapsed] = useState(
    CollapsibleStorage.isCollapsed(blockKey),
  );

  const toggleVisible = useCallback(() => {
    setIsCollapsed((prev) => {
      CollapsibleStorage.set(blockKey, !prev);
      return !prev;
    });
  }, [blockKey]);

  const onError = useCallback((error: Error) => {
    console.error("Error in Collapsible", error);
    CollapsibleStorage.set(blockKey, true);
    setIsCollapsed(true);
  }, [blockKey]);

  return {
    isCollapsed,
    toggleVisible,
    onError,
  };
};

export default function Collapsible({
  Title = "h4",
  blockKey: blockKeyProp,
  children,
  color,
  title,
  onIsCollapsedChange,
}: React.PropsWithChildren<{
  Title?: "h2" | "h3" | "h4";
  blockKey?: string;
  color?: string;
  title: string | React.ReactNode;
  onIsCollapsedChange?: (isCollapsed: boolean) => void;
}>) {
  const classes = useStyles();

  const { isCollapsed, toggleVisible, onError } = useCollapsible({
    blockKey: blockKeyProp,
    title,
  });

  useEffect(() => {
    if (onIsCollapsedChange) {
      onIsCollapsedChange(isCollapsed);
    }
  }, [isCollapsed, onIsCollapsedChange]);

  return (
    <>
      <button
        type="button"
        className={cn("items-center flex gap-m outline-none cursor-pointer w-full py-s px-m my-m bg-greyOpacity border-none", classes.collapsible)}
        onClick={toggleVisible}
        style={{
          ...(!TextUtil.isEmpty(color) && {
            background: color!,
          }),
        }}
      >
        {typeof title === "string" ? <Title>{title}</Title> : title}
      </button>
      <ComponentErrorBoundary onError={onError}>
        {!isCollapsed && children}
      </ComponentErrorBoundary>
    </>
  );
};
