import { useReducedMotion } from "motion/react";
import {
  Children,
  cloneElement,
  forwardRef,
  isValidElement,
  useEffect,
  useState,
  type ComponentPropsWithoutRef,
  type CSSProperties
} from "react";

import {
  emptyStateActionsVariants,
  emptyStateDescriptionVariants,
  emptyStateEyebrowVariants,
  emptyStateHeaderVariants,
  emptyStateMediaVariants,
  emptyStateTitleVariants,
  emptyStateVariants
} from "./empty-state.variants";
import { cn } from "../lib/cn";
import type { VariantProps } from "../lib/cva";
import { createDataAttributes, createSlot } from "../lib/contracts";

function getIsStaticMotion() {
  if (typeof document === "undefined") {
    return false;
  }

  return document.documentElement.dataset.motion === "static";
}

function useMotionDisabled() {
  const prefersReducedMotion = useReducedMotion();
  const [isStaticMotion, setIsStaticMotion] = useState(getIsStaticMotion);

  useEffect(() => {
    if (typeof document === "undefined") {
      return;
    }

    const syncMotionMode = () => {
      setIsStaticMotion(getIsStaticMotion());
    };

    syncMotionMode();

    const observer = new MutationObserver(syncMotionMode);
    observer.observe(document.documentElement, {
      attributeFilter: ["data-motion"]
    });

    return () => observer.disconnect();
  }, []);

  return prefersReducedMotion || isStaticMotion;
}

export type EmptyStateProps = ComponentPropsWithoutRef<"div"> &
  VariantProps<typeof emptyStateVariants>;

export const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(
  { align, className, layout, tone, ...props },
  ref
) {
  return (
    <div
      {...props}
      {...createSlot("root")}
      {...createDataAttributes({ align, layout, tone })}
      className={cn(emptyStateVariants({ align, layout, tone }), className)}
      ref={ref}
    />
  );
});

export type EmptyStateMediaProps = ComponentPropsWithoutRef<"div"> &
  VariantProps<typeof emptyStateMediaVariants>;

export const EmptyStateMedia = forwardRef<HTMLDivElement, EmptyStateMediaProps>(
  function EmptyStateMedia({ className, size, ...props }, ref) {
    const disableMotion = useMotionDisabled();
    const ambientMotionClassName =
      disableMotion || size === "compact"
        ? undefined
        : size === "hero"
          ? "motion-float-delayed will-change-transform"
          : "motion-breathe will-change-transform";

    return (
      <div
        {...props}
        {...createSlot("media")}
        {...createDataAttributes({ size })}
        className={cn(emptyStateMediaVariants({ size }), ambientMotionClassName, className)}
        ref={ref}
      />
    );
  }
);

export type EmptyStateHeaderProps = ComponentPropsWithoutRef<"div"> &
  VariantProps<typeof emptyStateHeaderVariants>;

export const EmptyStateHeader = forwardRef<HTMLDivElement, EmptyStateHeaderProps>(
  function EmptyStateHeader({ align, className, ...props }, ref) {
    return (
      <div
        {...props}
        {...createSlot("header")}
        {...createDataAttributes({ align })}
        className={cn(emptyStateHeaderVariants({ align }), className)}
        ref={ref}
      />
    );
  }
);

export type EmptyStateEyebrowProps = ComponentPropsWithoutRef<"p">;

export const EmptyStateEyebrow = forwardRef<HTMLParagraphElement, EmptyStateEyebrowProps>(
  function EmptyStateEyebrow({ className, ...props }, ref) {
    return (
      <p
        {...props}
        {...createSlot("eyebrow")}
        className={cn(emptyStateEyebrowVariants(), className)}
        ref={ref}
      />
    );
  }
);

export type EmptyStateTitleProps = ComponentPropsWithoutRef<"h3">;

export const EmptyStateTitle = forwardRef<HTMLHeadingElement, EmptyStateTitleProps>(
  function EmptyStateTitle({ className, ...props }, ref) {
    return (
      <h3
        {...props}
        {...createSlot("label")}
        className={cn(emptyStateTitleVariants(), className)}
        ref={ref}
      />
    );
  }
);

export type EmptyStateDescriptionProps = ComponentPropsWithoutRef<"p">;

export const EmptyStateDescription = forwardRef<
  HTMLParagraphElement,
  EmptyStateDescriptionProps
>(function EmptyStateDescription({ className, ...props }, ref) {
  return (
    <p
      {...props}
      {...createSlot("description")}
      className={cn(emptyStateDescriptionVariants(), className)}
      ref={ref}
    />
  );
});

export type EmptyStateActionsProps = ComponentPropsWithoutRef<"div"> &
  VariantProps<typeof emptyStateActionsVariants>;

export const EmptyStateActions = forwardRef<HTMLDivElement, EmptyStateActionsProps>(
  function EmptyStateActions({ children, className, layout, ...props }, ref) {
    const disableMotion = useMotionDisabled();
    const animatedChildren = disableMotion
      ? children
      : Children.map(children, (child, index) => {
          if (
            !isValidElement<{ className?: string; style?: CSSProperties }>(child) ||
            typeof child.type === "symbol"
          ) {
            return child;
          }

          return cloneElement(child, {
            className: cn(
              "[animation:aiui-slide-up-sm_var(--dur-base)_var(--ease-emphasized)_both]",
              "will-change-transform",
              child.props.className
            ),
            style: {
              ...(child.props.style ?? {}),
              animationDelay: `${Math.min(index * 70, 140)}ms`
            }
          });
        });

    return (
      <div
        {...props}
        {...createSlot("actions")}
        {...createDataAttributes({ layout })}
        className={cn(emptyStateActionsVariants({ layout }), className)}
        ref={ref}
      >
        {animatedChildren}
      </div>
    );
  }
);
