import * as React from 'react';
import { AlertTriangle } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Button } from './button';

export interface ConfirmDialogProps {
  cancelLabel?: string;
  children?: React.ReactNode;
  className?: string;
  confirmLabel?: string;
  confirmVariant?: 'default' | 'destructive';
  description?: string;
  loading?: boolean;
  open: boolean;
  title: string;
  onCancel: () => void;
  onConfirm: () => void | Promise<void>;
}

export function ConfirmDialog(props: ConfirmDialogProps) {
  const { open } = props;
  const titleId = React.useId();
  const descriptionId = React.useId();
  const dialogRef = React.useRef<HTMLElement>(null);
  const cancelRef = React.useRef<HTMLButtonElement>(null);
  const loadingRef = React.useRef(props.loading);
  const onCancelRef = React.useRef(props.onCancel);
  loadingRef.current = props.loading;
  onCancelRef.current = props.onCancel;

  React.useEffect(() => {
    if (!open) return undefined;
    const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    const focusFrame = window.requestAnimationFrame(() => {
      if (cancelRef.current && !cancelRef.current.disabled) cancelRef.current.focus();
      else dialogRef.current?.focus();
    });
    function onKeyDown(event: KeyboardEvent) {
      if (event.key === 'Escape' && !loadingRef.current) {
        event.preventDefault();
        onCancelRef.current();
        return;
      }
      if (event.key !== 'Tab' || !dialogRef.current) return;
      const focusable = Array.from(dialogRef.current.querySelectorAll<HTMLElement>('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
      if (!focusable.length) {
        event.preventDefault();
        dialogRef.current.focus();
        return;
      }
      const first = focusable[0];
      const last = focusable[focusable.length - 1];
      if (document.activeElement === dialogRef.current) {
        event.preventDefault();
        (event.shiftKey ? last : first).focus();
      } else if (!dialogRef.current.contains(document.activeElement)) {
        event.preventDefault();
        first.focus();
      } else if (event.shiftKey && document.activeElement === first) {
        event.preventDefault();
        last.focus();
      } else if (!event.shiftKey && document.activeElement === last) {
        event.preventDefault();
        first.focus();
      }
    }
    window.addEventListener('keydown', onKeyDown);
    return () => {
      window.cancelAnimationFrame(focusFrame);
      window.removeEventListener('keydown', onKeyDown);
      previouslyFocused?.focus();
    };
  }, [open]);

  React.useEffect(() => {
    if (open && props.loading) dialogRef.current?.focus();
  }, [open, props.loading]);

  if (!open) return null;

  return (
    <div className="confirmDialogBackdrop" role="presentation">
      <section ref={dialogRef} className={cn('confirmDialog', props.className)} role="alertdialog" tabIndex={-1} aria-busy={props.loading || undefined} aria-modal="true" aria-labelledby={titleId} aria-describedby={props.description ? descriptionId : undefined}>
        <div className="confirmDialogIcon" aria-hidden="true">
          <AlertTriangle size={18} />
        </div>
        <div className="confirmDialogBody">
          <strong id={titleId}>{props.title}</strong>
          {props.description && <p id={descriptionId}>{props.description}</p>}
          {props.children}
        </div>
        <footer className="confirmDialogActions">
          <Button ref={cancelRef} type="button" variant="outline" size="sm" disabled={props.loading} onClick={props.onCancel}>
            {props.cancelLabel ?? '取消'}
          </Button>
          <Button
            type="button"
            variant={props.confirmVariant ?? 'destructive'}
            size="sm"
            disabled={props.loading}
            onClick={() => void props.onConfirm()}
          >
            {props.confirmLabel ?? '确认'}
          </Button>
        </footer>
      </section>
    </div>
  );
}
