/**
 * Copyright (c) 2023-present Plane Software, Inc. and contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 * See the LICENSE file for details.
 */

import * as React from "react";
import ReactDOM from "react-dom";
import { usePopper } from "react-popper";
import { MoreHorizontal } from "lucide-react";
import { useOutsideClickDetector } from "@plane/hooks";
import { ChevronRightIcon } from "@plane/propel/icons";
import type { TPlacement } from "@plane/propel/utils/placement";
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
import { cn } from "../utils";
import type { TContextMenuItem } from "./context-menu";

export type TActionDropdownTrigger = React.ReactNode;

export interface IActionDropdownProps {
  button?: TActionDropdownTrigger;
  buttonClassName?: string;
  className?: string;
  items: TContextMenuItem[];
  menuClassName?: string;
  onOpenChange?: (isOpen: boolean) => void;
  placement?: TPlacement;
  portalElement?: Element | null;
}

const renderActionContent = (item: TContextMenuItem) =>
  item.customContent ?? (
    <div className="flex items-start gap-2">
      {item.icon && <item.icon className={cn("mt-0.5 h-3 w-3 shrink-0", item.iconClassName)} />}
      <div className="min-w-0 flex-1">
        {item.title && <h5 className="text-12 font-medium leading-5">{item.title}</h5>}
        {item.description && (
          <p
            className={cn("whitespace-pre-line text-tertiary", {
              "text-placeholder": item.disabled,
            })}
          >
            {item.description}
          </p>
        )}
      </div>
    </div>
  );

type TActionDropdownItemProps = {
  item: TContextMenuItem;
  onSelect: () => void;
};

function ActionDropdownItem(props: TActionDropdownItemProps) {
  const { item, onSelect } = props;
  const nestedItems = item.nestedMenuItems?.filter((nestedItem) => nestedItem.shouldRender !== false) ?? [];
  const hasNestedItems = nestedItems.length > 0;
  const [isNestedOpen, setIsNestedOpen] = React.useState(false);

  if (item.shouldRender === false) return null;

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    event.preventDefault();
    event.stopPropagation();

    if (item.disabled) return;

    if (hasNestedItems) {
      setIsNestedOpen((prev) => !prev);
      return;
    }

    item.action();
    onSelect();
  };

  return (
    <div className="space-y-1">
      <button
        type="button"
        className={cn(
          "flex w-full items-start gap-2 rounded-[0.9rem] px-2 py-2 text-left text-secondary select-none outline-none transition-colors",
          {
            "text-placeholder": item.disabled,
            "cursor-not-allowed": item.disabled,
            "hover:bg-white/6": !item.disabled,
          },
          item.className
        )}
        onClick={handleClick}
        disabled={item.disabled}
      >
        <span className="min-w-0 flex-1">{renderActionContent(item)}</span>
        {hasNestedItems && <ChevronRightIcon className={cn("mt-1 h-3.5 w-3.5 shrink-0 transition-transform", { "rotate-90": isNestedOpen })} />}
      </button>

      {hasNestedItems && isNestedOpen && (
        <div className="space-y-1 pl-3">
          {nestedItems.map((nestedItem) => (
            <ActionDropdownItem key={nestedItem.key} item={nestedItem} onSelect={onSelect} />
          ))}
        </div>
      )}
    </div>
  );
}

export function ActionDropdown(props: IActionDropdownProps) {
  const { button, buttonClassName, className, items, menuClassName, onOpenChange, placement, portalElement } = props;
  const dropdownRef = React.useRef<HTMLDivElement | null>(null);
  const [referenceElement, setReferenceElement] = React.useState<HTMLElement | null>(null);
  const [popperElement, setPopperElement] = React.useState<HTMLDivElement | null>(null);
  const [isOpen, setIsOpen] = React.useState(false);

  const renderedItems = items.filter((item) => item.shouldRender !== false);

  const { styles, attributes } = usePopper(referenceElement, popperElement, {
    placement: placement ?? "bottom-end",
    strategy: "fixed",
    modifiers: [
      {
        name: "offset",
        options: {
          offset: [0, 8],
        },
      },
      {
        name: "flip",
        options: {
          fallbackPlacements: ["bottom-end", "bottom-start", "top-end", "top-start"],
        },
      },
      {
        name: "preventOverflow",
        options: {
          padding: 12,
        },
      },
    ],
  });

  const openDropdown = React.useCallback(() => {
    if (renderedItems.length === 0) return;
    setIsOpen(true);
    onOpenChange?.(true);
  }, [onOpenChange, renderedItems.length]);

  const closeDropdown = React.useCallback(() => {
    if (!isOpen) return;
    setIsOpen(false);
    onOpenChange?.(false);
  }, [isOpen, onOpenChange]);

  const handleTriggerClick = (event: React.MouseEvent<HTMLElement>) => {
    event.preventDefault();
    event.stopPropagation();

    if (renderedItems.length === 0) return;

    if (isOpen) {
      closeDropdown();
      return;
    }

    openDropdown();
  };

  const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen);

  useOutsideClickDetector(dropdownRef, closeDropdown);

  const defaultButton = (
    <button
      ref={setReferenceElement as React.Ref<HTMLButtonElement>}
      type="button"
      className={cn(
        "clickable relative grid place-items-center rounded-sm border-0 bg-transparent p-1 text-secondary shadow-none outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-0 hover:text-primary",
        {
          "cursor-not-allowed": renderedItems.length === 0,
          "cursor-pointer": renderedItems.length > 0,
        },
        buttonClassName
      )}
      onClick={handleTriggerClick}
      onKeyDown={handleKeyDown}
      disabled={renderedItems.length === 0}
      aria-haspopup="menu"
      aria-expanded={isOpen}
      aria-label="Work item actions"
      data-action-dropdown-trigger="true"
    >
      <MoreHorizontal className="h-3.5 w-3.5" />
    </button>
  );

  const customButton = button ? (
    <button
      ref={setReferenceElement as React.Ref<HTMLButtonElement>}
      type="button"
      className={cn(
        "clickable block h-full rounded-full border-0 bg-transparent p-0 shadow-none outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-0",
        {
          "cursor-not-allowed": renderedItems.length === 0,
          "cursor-pointer": renderedItems.length > 0,
        },
        buttonClassName
      )}
      onClick={handleTriggerClick}
      onKeyDown={handleKeyDown}
      disabled={renderedItems.length === 0}
      aria-haspopup="menu"
      aria-expanded={isOpen}
      aria-label="Work item actions"
      data-action-dropdown-trigger="true"
    >
      {button}
    </button>
  ) : null;

  const popup =
    isOpen && typeof document !== "undefined"
      ? ReactDOM.createPortal(
          <div
            data-prevent-outside-click
            className="fixed z-[220]"
            ref={setPopperElement}
            style={styles.popper}
            {...attributes.popper}
          >
            <div
              className={cn(
                "nodedc-glass-modal nodedc-glass-popup-surface my-1 min-w-[13rem] rounded-[1.25rem] border-0 px-2 py-2.5 text-12 shadow-none outline-none",
                menuClassName
              )}
            >
              <div className="max-h-[min(85vh,40rem)] space-y-1 overflow-y-auto">
                {renderedItems.map((item) => (
                  <ActionDropdownItem key={item.key} item={item} onSelect={closeDropdown} />
                ))}
              </div>
            </div>
          </div>,
          portalElement ?? document.body
        )
      : null;

  return (
    <div ref={dropdownRef} className={cn("relative h-full", className)} onKeyDownCapture={handleKeyDown}>
      {customButton ?? defaultButton}
      {popup}
    </div>
  );
}
