import type { Middleware, Placement } from "@floating-ui/dom";
import { flip, offset, shift } from "@floating-ui/dom";
import type { Accessor, JSX } from "solid-js";
import { createSignal, createUniqueId, onCleanup, splitProps } from "solid-js";
import { createDisclosureState } from "../../primitives/create-disclosure-state";
import { createFloating } from "../../primitives/create-floating";
import { HoverCardContent } from "./hover-card-content";
import {
  HoverCardPublicContextProvider,
  InternalHoverCardContextProvider,
  useHoverCardContext,
} from "./hover-card-context";
import type { InternalHoverCardContextValue } from "./hover-card-context";
import { HoverCardTrigger } from "./hover-card-trigger";

import type { HoverCardRootProps } from "./hover-card.props";
export type { HoverCardRootProps } from "./hover-card.props";

/**
 * Root component for HoverCard. Manages open state, floating
 * positioning, and hover delay timers via context.
 */
export function HoverCardRoot(props: HoverCardRootProps): JSX.Element {
  const [local] = splitProps(props, [
    "open",
    "defaultOpen",
    "onOpenChange",
    "openDelay",
    "closeDelay",
    "children",
  ]);

  const triggerId = createUniqueId();
  const contentId = createUniqueId();

  const [triggerRef, setTriggerRef] = createSignal<HTMLElement | null>(null);
  const [contentRef, setContentRef] = createSignal<HTMLElement | null>(null);

  const openDelay = local.openDelay ?? 700;
  const closeDelay = local.closeDelay ?? 300;

  let openTimer: ReturnType<typeof setTimeout> | undefined;
  let closeTimer: ReturnType<typeof setTimeout> | undefined;

  const disclosure = createDisclosureState({
    get open() {
      return local.open;
    },
    get defaultOpen() {
      return local.defaultOpen;
    },
    get onOpenChange() {
      return local.onOpenChange;
    },
  });

  const floating = createFloating({
    anchor: triggerRef,
    floating: contentRef,
    placement: (() => "bottom") as Accessor<Placement>,
    middleware: (() => [offset(8), flip(), shift({ padding: 8 })]) as Accessor<Middleware[]>,
    open: disclosure.isOpen,
  });

  /** Cancel any pending open/close timer. */
  const cancelTimers = () => {
    if (openTimer !== undefined) {
      clearTimeout(openTimer);
      openTimer = undefined;
    }
    if (closeTimer !== undefined) {
      clearTimeout(closeTimer);
      closeTimer = undefined;
    }
  };

  /** Schedule open after openDelay. */
  const scheduleOpen = () => {
    cancelTimers();
    openTimer = setTimeout(() => {
      disclosure.open();
      openTimer = undefined;
    }, openDelay);
  };

  /** Schedule close after closeDelay. */
  const scheduleClose = () => {
    cancelTimers();
    closeTimer = setTimeout(() => {
      disclosure.close();
      closeTimer = undefined;
    }, closeDelay);
  };

  onCleanup(cancelTimers);

  const ctx: InternalHoverCardContextValue = {
    isOpen: disclosure.isOpen,
    open: disclosure.open,
    close: disclosure.close,
    triggerRef,
    setTriggerRef,
    contentRef,
    setContentRef,
    contentId,
    triggerId,
    floatingStyle: floating.style,
    openDelay,
    closeDelay,
    scheduleOpen,
    scheduleClose,
    cancelTimers,
  };

  return (
    <InternalHoverCardContextProvider value={ctx}>
      <HoverCardPublicContextProvider value={{ open: disclosure.isOpen }}>
        {local.children}
      </HoverCardPublicContextProvider>
    </InternalHoverCardContextProvider>
  );
}

/** Compound HoverCard component with all sub-components as static properties. */
export const HoverCard = Object.assign(HoverCardRoot, {
  Trigger: HoverCardTrigger,
  Content: HoverCardContent,
  useContext: useHoverCardContext,
});
