"use client";

import { type HTMLMotionProps, motion } from "motion/react";
import { Dialog as DialogPrimitive } from "radix-ui";
import type * as React from "react";
import { useTranslation } from "react-i18next";
import { RxCross2 } from "react-icons/rx";

import { useControlledState } from "@/hooks/use-controlled-state";
import { getStrictContext } from "@/lib/get-strict-context";
import { cn } from "@/lib/utils";
import { WindowDragArea } from "../window-drag-area";

type DialogContextType = {
  isOpen: boolean;
  setIsOpen: DialogProps["onOpenChange"];
  subPage: boolean;
  container: HTMLElement | null | undefined;
};

const [DialogProvider, useDialog] =
  getStrictContext<DialogContextType>("DialogContext");

type DialogProps = React.ComponentProps<typeof DialogPrimitive.Root> & {
  /** Render in a portal container as an in-flow sub-page instead of a centered modal. */
  subPage?: boolean;
  /** Portal container target. Required when subPage=true; ignored otherwise. */
  container?: HTMLElement | null;
};

function Dialog({ subPage, container, children, ...props }: DialogProps) {
  const [isOpen, setIsOpen] = useControlledState({
    value: props?.open,
    defaultValue: props?.defaultOpen,
    onChange: props?.onOpenChange,
  });

  return (
    <DialogProvider
      value={{
        isOpen,
        setIsOpen,
        subPage: !!subPage,
        container: container ?? undefined,
      }}
    >
      {/* In sub-page mode the Dialog isn't a modal — it's an in-flow page.
          Forcing `modal={false}` prevents Radix from locking pointer-events
          and aria-hiding everything outside the dialog. Children are passed
          explicitly (not via spread) so React doesn't have to guess where
          the JSX subtree should mount. */}
      <DialogPrimitive.Root
        data-slot="dialog"
        {...props}
        modal={subPage ? false : props.modal}
        onOpenChange={setIsOpen}
      >
        {children}
      </DialogPrimitive.Root>
    </DialogProvider>
  );
}

type DialogTriggerProps = React.ComponentProps<typeof DialogPrimitive.Trigger>;

function DialogTrigger(props: DialogTriggerProps) {
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}

type DialogPortalProps = Omit<
  React.ComponentProps<typeof DialogPrimitive.Portal>,
  "forceMount"
>;

function DialogPortal({
  children,
  container: portalContainer,
  ...props
}: DialogPortalProps) {
  const { isOpen, container } = useDialog();

  if (!isOpen) return null;

  return (
    <DialogPrimitive.Portal
      data-slot="dialog-portal"
      forceMount
      container={container ?? portalContainer}
      {...props}
    >
      {children}
    </DialogPrimitive.Portal>
  );
}

type DialogOverlayProps = Omit<
  React.ComponentProps<typeof DialogPrimitive.Overlay>,
  "forceMount" | "asChild"
> &
  HTMLMotionProps<"div">;

function DialogOverlay({
  className,
  transition = { duration: 0.2, ease: "easeInOut" },
  ...props
}: DialogOverlayProps) {
  return (
    <DialogPrimitive.Overlay data-slot="dialog-overlay" asChild forceMount>
      <motion.div
        key="dialog-overlay"
        initial={{ opacity: 0, filter: "blur(4px)" }}
        animate={{ opacity: 1, filter: "blur(0px)" }}
        transition={transition}
        className={cn("fixed inset-0 z-9999 bg-background/50", className)}
        {...props}
      >
        {/* Keep the OS title-bar zone draggable while a modal is open — the
            overlay otherwise covers the native drag region. `data-window-drag-area`
            stops Radix from treating a drag here as an outside-click dismiss. */}
        <div
          data-tauri-drag-region
          data-window-drag-area="true"
          aria-hidden="true"
          className="absolute inset-x-0 top-0 h-11"
        />
        <WindowDragArea />
      </motion.div>
    </DialogPrimitive.Overlay>
  );
}

type DialogContentProps = Omit<
  React.ComponentProps<typeof DialogPrimitive.Content>,
  "forceMount" | "asChild"
> &
  HTMLMotionProps<"div"> & {
    /**
     * Suppress the built-in top-right close X. Use when the dialog renders
     * its own header bar with a custom close control to avoid two X buttons
     * stacking near the corner.
     */
    hideClose?: boolean;
    /**
     * When false, the user cannot dismiss the dialog — Escape and outside
     * clicks are ignored and the close X is hidden. Use for steps the user
     * must complete to progress (e.g. required onboarding, a blocking
     * download). The dialog can still be closed programmatically via `open`.
     */
    dismissible?: boolean;
  };

function SubPageContent({
  children,
}: {
  className?: string;
  children?: React.ReactNode;
}) {
  const { isOpen } = useDialog();
  if (!isOpen) return null;
  // Inline styles deliberately override any className the caller passed
  // for the modal mode (max-w-*, max-h-*, my-*). tailwind-merge inside the
  // shared dialog wrappers turned out to be unreliable when both classnames
  // and !important variants competed — inline styles guarantee the layout.
  return (
    <motion.div
      data-slot="sub-page"
      data-sub-page="true"
      // Sub-pages enter with a short rise+fade so rail navigation reads as a
      // transition instead of a hard cut. Same axis for every page (spatial
      // consistency); the outgoing page unmounts under the incoming fade.
      initial={{ opacity: 0, y: 8 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.25, ease: [0.23, 1, 0.32, 1] }}
      style={{
        position: "relative",
        display: "flex",
        flexDirection: "column",
        flex: "1 1 0%",
        minHeight: 0,
        width: "100%",
        maxWidth: "none",
        height: "100%",
        maxHeight: "none",
        margin: 0,
        padding: 12,
        gap: 12,
        // The sub-page wrapper never scrolls itself — exactly one inner
        // element per page owns scrolling (full-width, so the wheel works
        // over side gutters too). "auto" here created a competing,
        // never-engaged scroll container.
        overflow: "hidden",
        background: "var(--background)",
        containerType: "inline-size",
      }}
    >
      {children}
    </motion.div>
  );
}

function DialogContent({
  className,
  children,
  onOpenAutoFocus,
  onCloseAutoFocus,
  onEscapeKeyDown,
  onPointerDownOutside,
  onInteractOutside,
  transition,
  hideClose,
  dismissible = true,
  ...props
}: DialogContentProps) {
  const { t } = useTranslation();
  const { subPage } = useDialog();

  if (subPage) {
    return <SubPageContent>{children}</SubPageContent>;
  }

  return (
    <DialogPortal data-slot="dialog-portal">
      <DialogOverlay key="dialog-overlay" />
      <DialogPrimitive.Content
        key="dialog-content"
        asChild
        forceMount
        onOpenAutoFocus={onOpenAutoFocus}
        onCloseAutoFocus={onCloseAutoFocus}
        onEscapeKeyDown={(event) => {
          if (!dismissible) event.preventDefault();
          onEscapeKeyDown?.(event);
        }}
        onPointerDownOutside={onPointerDownOutside}
        onInteractOutside={(event) => {
          if (!dismissible) {
            event.preventDefault();
            return;
          }
          const target = event.target as HTMLElement | null;
          if (target?.closest('[data-window-drag-area="true"]')) {
            event.preventDefault();
          }
          onInteractOutside?.(event);
        }}
      >
        <motion.div
          key="dialog-content"
          data-slot="dialog-content"
          // Open motion modeled on transitions.dev's modal: a subtle scale
          // from 0.96 → 1 with opacity, eased with cubic-bezier(0.22, 1, 0.36,
          // 1). The portal unmounts immediately on close so a closed Radix
          // surface cannot linger over the app. The centering translate stays
          // in `style` so `scale` animates around the center without fighting
          // the transform-based positioning.
          style={{ transformOrigin: "center" }}
          initial={{ opacity: 0, scale: 0.96 }}
          animate={{ opacity: 1, scale: 1 }}
          transition={
            transition ?? { duration: 0.25, ease: [0.22, 1, 0.36, 1] }
          }
          className={cn(
            // w-[calc(100%-2rem)] (not w-full + max-w) keeps the 1rem window
            // gutter even when callers override max-w-*: tailwind-merge drops
            // a base max-w in favor of the caller's, but leaves width alone.
            "surface-material fixed top-[50%] left-[50%] z-10000 grid max-h-[calc(100dvh-3rem)] w-[calc(100%-2rem)] max-w-lg -translate-[50%] gap-4 overflow-y-auto rounded-lg border p-6 shadow-lg",
            className,
          )}
          {...props}
        >
          {children}
          {!hideClose && dismissible && (
            <DialogPrimitive.Close className="absolute top-4 right-4 cursor-pointer rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
              <RxCross2 />
              <span className="sr-only">{t("common.buttons.close")}</span>
            </DialogPrimitive.Close>
          )}
        </motion.div>
      </DialogPrimitive.Content>
    </DialogPortal>
  );
}

type DialogCloseProps = React.ComponentProps<typeof DialogPrimitive.Close>;

function DialogClose(props: DialogCloseProps) {
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}

function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="dialog-header"
      className={cn("flex flex-col gap-2 pr-8 text-left", className)}
      {...props}
    />
  );
}

function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="dialog-footer"
      className={cn(
        "flex shrink-0 flex-row flex-wrap justify-end gap-2",
        className,
      )}
      {...props}
    />
  );
}

function DialogTitle({
  className,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
  return (
    <DialogPrimitive.Title
      data-slot="dialog-title"
      className={cn("text-lg leading-none font-semibold", className)}
      {...props}
    />
  );
}

function DialogDescription({
  className,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
  return (
    <DialogPrimitive.Description
      data-slot="dialog-description"
      className={cn("text-sm text-muted-foreground", className)}
      {...props}
    />
  );
}

export {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogOverlay,
  DialogPortal,
  DialogTitle,
  DialogTrigger,
};
