"use client";

import type { ReactElement, ReactNode } from "react";
import type { LucideIcon } from "lucide-react";

import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { cn } from "@/lib/utils";

export type ModalSize = "sm" | "md" | "lg" | "xl";

/** Widths: sm confirmations, md short forms, lg longer forms and details, xl tables and side-by-side content. */
const WIDTH: Record<ModalSize, string> = {
  sm: "sm:max-w-sm", // 384 px
  md: "sm:max-w-lg", // 512 px
  lg: "sm:max-w-2xl", // 672 px
  xl: "sm:max-w-4xl", // 896 px
};

export type ModalProps = {
  /** Controlled open state. Leave both unset and pass `trigger` for an uncontrolled modal. */
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
  /** Element that opens the modal, e.g. <Button />. Rendered as the trigger, so it keeps focus return and ARIA wiring. */
  trigger?: ReactElement;
  triggerLabel?: ReactNode;
  title: ReactNode;
  description?: ReactNode;
  /** Shown in a round chip beside the title. */
  icon?: LucideIcon;
  /** `destructive` colours the icon chip red; pair it with a destructive confirm button in the footer. */
  tone?: "default" | "destructive";
  size?: ModalSize;
  /** Footer actions, right-aligned. Use <ModalClose render={<Button variant="outline" />}> for Cancel. */
  footer?: ReactNode;
  /**
   * When false, clicking the backdrop does not close the modal and the corner close button is hidden
   * (for unsaved forms and payments). Escape and an explicit Cancel still work.
   */
  dismissible?: boolean;
  children?: ReactNode;
  className?: string;
};

/**
 * Medora modal: a centred dialog over a #001110 at 50% backdrop, with a fixed header, a body that scrolls on
 * its own when content is long, and a pinned footer. Built on the Base UI Dialog, so focus is trapped,
 * Escape closes it and focus returns to the trigger.
 */
export function Modal({
  open,
  onOpenChange,
  trigger,
  triggerLabel,
  title,
  description,
  icon: Icon,
  tone = "default",
  size = "md",
  footer,
  dismissible = true,
  children,
  className,
}: ModalProps) {
  return (
    <Dialog open={open} onOpenChange={onOpenChange ? (o) => onOpenChange(o) : undefined} disablePointerDismissal={!dismissible}>
      {trigger ? <DialogTrigger render={trigger}>{triggerLabel}</DialogTrigger> : null}
      <DialogContent
        showCloseButton={dismissible}
        className={cn("flex max-h-[min(85dvh,calc(100dvh-2rem))] flex-col gap-0 overflow-hidden p-0", WIDTH[size], className)}
      >
        <div className={cn("flex shrink-0 items-start gap-3 px-5 pt-5", children ? "pb-4" : "pb-5", dismissible && "pr-12")}>
          {Icon ? (
            <span
              className={cn(
                "flex size-9 shrink-0 items-center justify-center rounded-full [&_svg]:size-4.5",
                tone === "destructive" ? "bg-status-error-bg text-status-error-text" : "bg-accent text-accent-foreground",
              )}
              aria-hidden
            >
              <Icon />
            </span>
          ) : null}
          <div className="grid min-w-0 gap-1.5">
            {/* heading-lg (18/24 semibold) as standard utilities: cn cannot tell the custom text-heading-lg
                conflicts with the base text-base, so the base would win. */}
            <DialogTitle className="text-lg leading-6 font-semibold">{title}</DialogTitle>
            {description ? <DialogDescription>{description}</DialogDescription> : null}
          </div>
        </div>

        {children ? <div className="min-h-0 flex-1 overflow-y-auto px-5 pb-5">{children}</div> : null}

        {footer ? <DialogFooter className="m-0 shrink-0 rounded-b-xl px-5 py-3">{footer}</DialogFooter> : null}
      </DialogContent>
    </Dialog>
  );
}

/** Closes the surrounding Modal. Render it as a button: <ModalClose render={<Button variant="outline" />}>Cancel</ModalClose>. */
export const ModalClose = DialogClose;
