import { isDevelopment } from "#is-development";
import { PanelData } from "../Panel";
import { Direction } from "../types";
import { adjustLayoutByDelta } from "../utils/adjustLayoutByDelta";
import { assert } from "../utils/assert";
import { calculateAriaValues } from "../utils/calculateAriaValues";
import { determinePivotIndices } from "../utils/determinePivotIndices";
import { getPanelGroupElement } from "../utils/dom/getPanelGroupElement";
import { getResizeHandleElementsForGroup } from "../utils/dom/getResizeHandleElementsForGroup";
import { getResizeHandlePanelIds } from "../utils/dom/getResizeHandlePanelIds";
import { fuzzyNumbersEqual } from "../utils/numbers/fuzzyNumbersEqual";
import { RefObject, useEffect, useRef } from "../vendor/react";
import useIsomorphicLayoutEffect from "./useIsomorphicEffect";

// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/

export function useWindowSplitterPanelGroupBehavior({
  committedValuesRef,
  eagerValuesRef,
  groupId,
  layout,
  panelDataArray,
  panelGroupElement,
  setLayout,
}: {
  committedValuesRef: RefObject<{
    direction: Direction;
  }>;
  eagerValuesRef: RefObject<{
    panelDataArray: PanelData[];
  }>;
  groupId: string;
  layout: number[];
  panelDataArray: PanelData[];
  panelGroupElement: ParentNode | null;
  setLayout: (sizes: number[]) => void;
}): void {
  const devWarningsRef = useRef<{
    didWarnAboutMissingResizeHandle: boolean;
  }>({
    didWarnAboutMissingResizeHandle: false,
  });

  useIsomorphicLayoutEffect(() => {
    if (!panelGroupElement) {
      return;
    }
    const resizeHandleElements = getResizeHandleElementsForGroup(
      groupId,
      panelGroupElement
    );

    for (let index = 0; index < panelDataArray.length - 1; index++) {
      const { valueMax, valueMin, valueNow } = calculateAriaValues({
        layout,
        panelsArray: panelDataArray,
        pivotIndices: [index, index + 1],
      });

      const resizeHandleElement = resizeHandleElements[index];
      if (resizeHandleElement == null) {
        if (isDevelopment) {
          const { didWarnAboutMissingResizeHandle } = devWarningsRef.current;

          if (!didWarnAboutMissingResizeHandle) {
            devWarningsRef.current.didWarnAboutMissingResizeHandle = true;

            console.warn(
              `WARNING: Missing resize handle for PanelGroup "${groupId}"`
            );
          }
        }
      } else {
        const panelData = panelDataArray[index];
        assert(panelData, `No panel data found for index "${index}"`);

        resizeHandleElement.setAttribute("aria-controls", panelData.id);
        resizeHandleElement.setAttribute(
          "aria-valuemax",
          "" + Math.round(valueMax)
        );
        resizeHandleElement.setAttribute(
          "aria-valuemin",
          "" + Math.round(valueMin)
        );
        resizeHandleElement.setAttribute(
          "aria-valuenow",
          valueNow != null ? "" + Math.round(valueNow) : ""
        );
      }
    }

    return () => {
      resizeHandleElements.forEach((resizeHandleElement, index) => {
        resizeHandleElement.removeAttribute("aria-controls");
        resizeHandleElement.removeAttribute("aria-valuemax");
        resizeHandleElement.removeAttribute("aria-valuemin");
        resizeHandleElement.removeAttribute("aria-valuenow");
      });
    };
  }, [groupId, layout, panelDataArray, panelGroupElement]);

  useEffect(() => {
    if (!panelGroupElement) {
      return;
    }
    const eagerValues = eagerValuesRef.current;
    assert(eagerValues, `Eager values not found`);

    const { panelDataArray } = eagerValues;
    const groupElement = getPanelGroupElement(groupId, panelGroupElement);
    assert(groupElement != null, `No group found for id "${groupId}"`);

    const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
    assert(handles, `No resize handles found for group id "${groupId}"`);

    const cleanupFunctions = handles.map((handle) => {
      const handleId = handle.getAttribute("data-panel-resize-handle-id");
      assert(handleId, `Resize handle element has no handle id attribute`);

      const [idBefore, idAfter] = getResizeHandlePanelIds(
        groupId,
        handleId,
        panelDataArray,
        panelGroupElement
      );
      if (idBefore == null || idAfter == null) {
        return () => {};
      }

      const onKeyDown = (event: KeyboardEvent) => {
        if (event.defaultPrevented) {
          return;
        }

        switch (event.key) {
          case "Enter": {
            event.preventDefault();

            const index = panelDataArray.findIndex(
              (panelData) => panelData.id === idBefore
            );
            if (index >= 0) {
              const panelData = panelDataArray[index];
              assert(panelData, `No panel data found for index ${index}`);

              const size = layout[index];

              const {
                collapsedSize = 0,
                collapsible,
                minSize = 0,
              } = panelData.constraints;

              if (size != null && collapsible) {
                const nextLayout = adjustLayoutByDelta({
                  delta: fuzzyNumbersEqual(size, collapsedSize)
                    ? minSize - collapsedSize
                    : collapsedSize - size,
                  initialLayout: layout,
                  panelConstraints: panelDataArray.map(
                    (panelData) => panelData.constraints
                  ),
                  pivotIndices: determinePivotIndices(
                    groupId,
                    handleId,
                    panelGroupElement
                  ),
                  prevLayout: layout,
                  trigger: "keyboard",
                });
                if (layout !== nextLayout) {
                  setLayout(nextLayout);
                }
              }
            }
            break;
          }
        }
      };

      handle.addEventListener("keydown", onKeyDown);

      return () => {
        handle.removeEventListener("keydown", onKeyDown);
      };
    });

    return () => {
      cleanupFunctions.forEach((cleanupFunction) => cleanupFunction());
    };
  }, [
    panelGroupElement,
    committedValuesRef,
    eagerValuesRef,
    groupId,
    layout,
    panelDataArray,
    setLayout,
  ]);
}
