import * as React from 'react';
import { cn } from '@/lib/utils';
import { buttonVariants } from '@/components/ui/button';
import { BusyButton } from '@/components/ui/busy-button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogTitle,
} from '@/components/ui/dialog';
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogTitle,
} from '@/components/ui/alert-dialog';

const KICKER_CLASS = 'font-mono text-[10px] uppercase tracking-[0.22em]';

type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'wide';

const SIZE_CLASS: Record<ModalSize, string> = {
  sm: 'max-w-sm',
  md: 'max-w-md',
  lg: 'max-w-lg',
  xl: 'max-w-xl w-[95vw]',
  wide: 'max-w-5xl w-[95vw]',
};

interface ModalProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  children: React.ReactNode;
  size?: ModalSize;
  className?: string;
  /** Pass through to DialogContent. Set to false when the modal renders its own close affordance. */
  showClose?: boolean;
  /** Fill the viewport below md (for large overlays like logs / shell). Opt-in so
   *  small confirm dialogs keep their compact centered size on a phone. */
  mobileFullScreen?: boolean;
}

export function Modal({ open, onOpenChange, children, size = 'md', className, showClose, mobileFullScreen }: ModalProps) {
  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        showClose={showClose}
        className={cn(
          'p-0 gap-0 overflow-hidden grid-cols-1',
          SIZE_CLASS[size],
          className,
          mobileFullScreen && 'max-md:h-[100dvh] max-md:w-screen max-md:max-w-none max-md:rounded-none max-md:border-0',
        )}
      >
        {children}
      </DialogContent>
    </Dialog>
  );
}

interface ModalHeaderBaseProps {
  kicker: string;
  title: React.ReactNode;
  description?: string;
}

type HeaderVariant = 'default' | 'destructive';

const HEADER_VARIANT: Record<HeaderVariant, { rail: string; kicker: string }> = {
  default: { rail: 'bg-brand', kicker: 'text-stat-subtitle' },
  destructive: { rail: 'bg-destructive', kicker: 'text-destructive' },
};

interface HeaderShellProps extends ModalHeaderBaseProps {
  variant: HeaderVariant;
  TitleComponent: React.ElementType;
  DescriptionComponent: React.ElementType;
}

function HeaderShell({
  kicker,
  title,
  description,
  variant,
  TitleComponent,
  DescriptionComponent,
}: HeaderShellProps) {
  const v = HEADER_VARIANT[variant];
  return (
    <div className="relative shrink-0 border-b border-card-border/60 px-6 pt-6 pb-4 pr-12">
      <span aria-hidden className={cn('absolute inset-y-0 left-0 w-[3px]', v.rail)} />
      <div className={cn(KICKER_CLASS, 'truncate', v.kicker)} title={kicker}>
        {kicker}
      </div>
      <TitleComponent
        className="mt-1 font-heading text-[1.75rem] leading-tight text-stat-value break-all line-clamp-3"
        title={typeof title === 'string' ? title : undefined}
      >
        {title}
      </TitleComponent>
      <DescriptionComponent className="sr-only">
        {description ?? (typeof title === 'string' ? title : kicker)}
      </DescriptionComponent>
    </div>
  );
}

export function ModalHeader(props: ModalHeaderBaseProps) {
  return (
    <HeaderShell
      {...props}
      variant="default"
      TitleComponent={DialogTitle}
      DescriptionComponent={DialogDescription}
    />
  );
}

export function ModalDestructiveHeader(props: ModalHeaderBaseProps) {
  return (
    <HeaderShell
      {...props}
      variant="destructive"
      TitleComponent={DialogTitle}
      DescriptionComponent={DialogDescription}
    />
  );
}

function ConfirmHeader(props: ModalHeaderBaseProps) {
  return (
    <HeaderShell
      {...props}
      variant="default"
      TitleComponent={AlertDialogTitle}
      DescriptionComponent={AlertDialogDescription}
    />
  );
}

function ConfirmDestructiveHeader(props: ModalHeaderBaseProps) {
  return (
    <HeaderShell
      {...props}
      variant="destructive"
      TitleComponent={AlertDialogTitle}
      DescriptionComponent={AlertDialogDescription}
    />
  );
}

interface ModalBodyProps extends React.HTMLAttributes<HTMLDivElement> {
  /** Grow within a flex-column confirm shell; body scrolls instead of stretching the dialog. */
  fill?: boolean;
}

export function ModalBody({ className, fill, ...props }: ModalBodyProps) {
  return (
    <div
      className={cn(
        'px-6 py-5 space-y-4 overflow-y-auto',
        fill ? 'min-h-0 flex-1' : 'max-h-[calc(85vh-12rem)]',
        className,
      )}
      {...props}
    />
  );
}

interface ModalFooterProps {
  primary: React.ReactNode;
  secondary?: React.ReactNode;
  hint?: React.ReactNode;
  hintAccent?: React.ReactNode;
}

export function ModalFooter({ primary, secondary, hint, hintAccent }: ModalFooterProps) {
  return (
    <div className="flex shrink-0 items-center justify-between gap-4 border-t border-card-border/60 px-6 py-4">
      <div className={cn(KICKER_CLASS, 'text-stat-subtitle')}>
        {hint}
        {hintAccent !== undefined && (
          <span className="ml-1.5 rounded-sm border border-card-border bg-card px-1.5 py-0.5 text-stat-value">
            {hintAccent}
          </span>
        )}
      </div>
      <div className="flex items-center gap-2">
        {secondary}
        {primary}
      </div>
    </div>
  );
}

type ConfirmSize = 'sm' | 'md';

interface ConfirmModalProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  variant?: 'default' | 'destructive';
  size?: ConfirmSize;
  kicker: string;
  title: React.ReactNode;
  description?: string;
  hint?: React.ReactNode;
  confirmLabel: React.ReactNode;
  /**
   * Progressive confirm copy shown only after delayed visual busy
   * (see BusyButton / useVisualBusy). Prefer static confirmLabel + this over
   * swapping confirmLabel when confirming flips true.
   */
  busyConfirmLabel?: React.ReactNode;
  cancelLabel?: React.ReactNode;
  confirming?: boolean;
  /** When true, the confirm action stays disabled (e.g. plan still loading). */
  confirmDisabled?: boolean;
  onConfirm: () => void | Promise<void>;
  onCancel?: () => void;
  children?: React.ReactNode;
}

export function ConfirmModal({
  open,
  onOpenChange,
  variant = 'default',
  size = 'sm',
  kicker,
  title,
  description,
  hint,
  confirmLabel,
  busyConfirmLabel,
  cancelLabel = 'Cancel',
  confirming = false,
  confirmDisabled = false,
  onConfirm,
  onCancel,
  children,
}: ConfirmModalProps) {
  const Header = variant === 'destructive' ? ConfirmDestructiveHeader : ConfirmHeader;
  const cancelClass = buttonVariants({ variant: 'outline', size: 'sm' });
  const actionVariant = variant === 'destructive' ? 'destructive' : 'default';
  const actionDisabled = confirming || confirmDisabled;

  const handleOpenChange = (next: boolean) => {
    // Block Esc / overlay dismiss while the confirm action owns the request.
    if (!next && confirming) return;
    onOpenChange(next);
  };

  return (
    <AlertDialog open={open} onOpenChange={handleOpenChange}>
      <AlertDialogContent className={cn('flex max-h-[85dvh] flex-col p-0 gap-0 overflow-hidden border-card-border/60', SIZE_CLASS[size])}>
        <Header kicker={kicker} title={title} description={description} />
        {children !== undefined && <ModalBody fill>{children}</ModalBody>}
        <ModalFooter
          hint={hint}
          secondary={
            <AlertDialogCancel
              className={cancelClass}
              disabled={confirming}
              onClick={onCancel}
            >
              {cancelLabel}
            </AlertDialogCancel>
          }
          primary={
            // BusyButton never uses asChild; Radix owns asChild on the Action
            // shell so the multi-child busy chrome stays on the real button.
            <AlertDialogAction asChild disabled={actionDisabled}>
              <BusyButton
                variant={actionVariant}
                size="sm"
                pending={confirming}
                busyLabel={busyConfirmLabel}
                disabled={actionDisabled}
                onClick={(e) => {
                  const result = onConfirm();
                  // Async confirms keep the dialog open so the caller can render
                  // `confirming` state and close via onOpenChange when work completes.
                  // Sync confirms let Radix auto-close.
                  if (result instanceof Promise) {
                    e.preventDefault();
                    void result;
                  }
                }}
              >
                {confirmLabel}
              </BusyButton>
            </AlertDialogAction>
          }
        />
      </AlertDialogContent>
    </AlertDialog>
  );
}
