// DSSheet.jsx — Meridian modal surfaces
//
//   DSBottomSheet — slide-up modal, drag-handle, scrim, content slot
//                   (height is content-driven; caller controls max-height)
//   DSDialog      — centered modal, title + optional message + button row
//                   isDangerous flips primary to crisis tone
//   DSTooltip     — small dark callout, used adjacent to terms / metrics
//
// Both modals share:
//   - Tokens.Color.overlay (40% black) scrim
//   - Esc + scrim-tap to dismiss
//   - Body scroll-lock while open
//   - 220ms cubic-bezier(.2,.8,.2,1) enter/exit

// ── Shared modal primitives ──────────────────────────────────────
function useModalKeybindings(isOpen, onClose) {
  React.useEffect(() => {
    if (!isOpen) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose?.(); };
    document.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [isOpen, onClose]);
}

function ModalScrim({ isOpen, onClose, children, align = 'center' }) {
  // Mount/unmount with a small delay so leave transitions can play.
  const [mounted, setMounted] = React.useState(isOpen);
  const [animateIn, setAnimateIn] = React.useState(false);
  React.useEffect(() => {
    if (isOpen) {
      setMounted(true);
      // Next frame so transition triggers from false → true
      requestAnimationFrame(() => setAnimateIn(true));
    } else {
      setAnimateIn(false);
      const t = setTimeout(() => setMounted(false), 220);
      return () => clearTimeout(t);
    }
  }, [isOpen]);

  if (!mounted) return null;

  return (
    <div
      role="presentation"
      onClick={(e) => { if (e.target === e.currentTarget) onClose?.(); }}
      style={{
        position: 'fixed', inset: 0,
        zIndex: 1000,
        background: 'rgba(0,0,0,0.40)',
        opacity: animateIn ? 1 : 0,
        transition: 'opacity 220ms cubic-bezier(.2,.8,.2,1)',
        display: 'flex',
        alignItems: align === 'bottom' ? 'flex-end' : 'center',
        justifyContent: 'center',
      }}
    >
      {typeof children === 'function' ? children(animateIn) : children}
    </div>
  );
}

// ── DSBottomSheet ────────────────────────────────────────────────
function DSBottomSheet({
  isOpen,
  onClose,
  children,
  title,           // optional — rendered between handle and content
  maxHeight = '85vh',
  closeOnScrimTap = true,
}) {
  useModalKeybindings(isOpen, onClose);
  // Drag-to-dismiss
  const [dragY, setDragY] = React.useState(0);
  const startY = React.useRef(null);

  const onPointerDown = (e) => {
    startY.current = e.clientY ?? e.touches?.[0]?.clientY;
  };
  const onPointerMove = (e) => {
    if (startY.current == null) return;
    const y = e.clientY ?? e.touches?.[0]?.clientY;
    const d = Math.max(0, y - startY.current);
    setDragY(d);
  };
  const onPointerUp = () => {
    if (dragY > 80) onClose?.();
    setDragY(0);
    startY.current = null;
  };

  return (
    <ModalScrim isOpen={isOpen} onClose={closeOnScrimTap ? onClose : undefined} align="bottom">
      {(animateIn) => (
        <div
          role="dialog" aria-modal="true"
          onClick={(e) => e.stopPropagation()}
          style={{
            width: '100%',
            maxWidth: 720,
            maxHeight,
            background: M.surface,
            borderTopLeftRadius: 20,
            borderTopRightRadius: 20,
            boxShadow: '0 -8px 40px rgba(14,18,32,0.18)',
            transform: `translateY(${animateIn ? dragY : '100%'})`,
            opacity: animateIn ? 1 : 0,
            transition: dragY ? 'none' : 'transform 280ms cubic-bezier(.2,.8,.2,1), opacity 220ms cubic-bezier(.2,.8,.2,1)',
            display: 'flex',
            flexDirection: 'column',
            overflow: 'hidden',
          }}
        >
          {/* Drag handle */}
          <div
            onMouseDown={onPointerDown}
            onMouseMove={startY.current != null ? onPointerMove : undefined}
            onMouseUp={onPointerUp}
            onMouseLeave={startY.current != null ? onPointerUp : undefined}
            onTouchStart={onPointerDown}
            onTouchMove={onPointerMove}
            onTouchEnd={onPointerUp}
            style={{
              padding: '12px 0 8px',
              cursor: 'grab',
              flexShrink: 0,
              display: 'flex',
              justifyContent: 'center',
              touchAction: 'none',
            }}
            aria-hidden="true"
          >
            <div style={{
              width: 36, height: 4, borderRadius: 999,
              background: M.rule,
            }} />
          </div>

          {title && (
            <div style={{
              padding: '4px 18px 12px',
              borderBottom: `1px solid ${M.rule}`,
              flexShrink: 0,
            }}>
              <div style={{
                font: `500 16px/1.3 ${M.sans}`,
                color: M.ink,
              }}>{title}</div>
            </div>
          )}

          <div style={{
            padding: '14px 18px 22px',
            overflowY: 'auto',
            WebkitOverflowScrolling: 'touch',
          }}>
            {children}
          </div>
        </div>
      )}
    </ModalScrim>
  );
}

// ── DSDialog ─────────────────────────────────────────────────────
function DSDialog({
  isOpen,
  onClose,
  title,
  message,
  secondaryLabel = 'Cancel',
  primaryLabel = 'OK',
  isDangerous = false,
  onPrimary,
  onSecondary,
  closeOnScrimTap = false, // dialogs require explicit choice by default
}) {
  useModalKeybindings(isOpen, onClose);
  const primaryVariant = isDangerous ? 'danger' : 'primary';
  const handleSecondary = () => {
    onSecondary?.();
    onClose?.();
  };
  const handlePrimary = () => {
    onPrimary?.();
    onClose?.();
  };

  return (
    <ModalScrim
      isOpen={isOpen}
      onClose={closeOnScrimTap ? onClose : undefined}
      align="center"
    >
      {(animateIn) => (
        <div
          role="alertdialog" aria-modal="true"
          aria-labelledby="dsdialog-title"
          onClick={(e) => e.stopPropagation()}
          style={{
            width: 'min(92vw, 360px)',
            background: M.surface,
            borderRadius: 20,
            boxShadow: '0 16px 40px rgba(14,18,32,0.18), 0 2px 6px rgba(14,18,32,0.08)',
            transform: animateIn ? 'scale(1)' : 'scale(0.96)',
            opacity: animateIn ? 1 : 0,
            transition: 'transform 220ms cubic-bezier(.2,.8,.2,1), opacity 220ms cubic-bezier(.2,.8,.2,1)',
            display: 'flex',
            flexDirection: 'column',
            overflow: 'hidden',
          }}
        >
          <div style={{ padding: '18px 18px 16px' }}>
            <div id="dsdialog-title" style={{
              font: `500 16px/1.3 ${M.sans}`,
              color: M.ink,
              marginBottom: message ? 8 : 0,
            }}>{title}</div>
            {message && (
              <div style={{
                font: `400 14px/1.5 ${M.sans}`,
                color: M.inkSoft,
              }}>{message}</div>
            )}
          </div>

          <div style={{
            borderTop: `1px solid ${M.rule}`,
            padding: 14,
            display: 'flex',
            gap: 10,
            justifyContent: 'flex-end',
          }}>
            <DSButton
              variant="secondary"
              onClick={handleSecondary}
              style={{ borderRadius: 10 }}
            >{secondaryLabel}</DSButton>
            <DSButton
              variant={primaryVariant}
              onClick={handlePrimary}
              style={{ borderRadius: 10 }}
            >{primaryLabel}</DSButton>
          </div>
        </div>
      )}
    </ModalScrim>
  );
}

// ── DSTooltip (parity with iOS) ──────────────────────────────────
function DSTooltip({ text, children }) {
  // Hover/focus to show. Positioned above the trigger.
  const [show, setShow] = React.useState(false);
  return (
    <span
      onMouseEnter={() => setShow(true)}
      onMouseLeave={() => setShow(false)}
      onFocus={() => setShow(true)}
      onBlur={() => setShow(false)}
      style={{ position: 'relative', display: 'inline-flex' }}
    >
      {children}
      <span role="tooltip" aria-hidden={!show} style={{
        position: 'absolute',
        bottom: 'calc(100% + 8px)',
        left: '50%',
        transform: `translate(-50%, ${show ? 0 : 4}px)`,
        background: M.ink, color: '#fff',
        padding: '6px 10px',
        borderRadius: 8,
        font: `400 12px/1.3 ${M.sans}`,
        whiteSpace: 'nowrap',
        boxShadow: '0 4px 12px rgba(14,18,32,0.20)',
        opacity: show ? 1 : 0,
        pointerEvents: 'none',
        transition: 'opacity 120ms cubic-bezier(.2,.8,.2,1), transform 120ms cubic-bezier(.2,.8,.2,1)',
        zIndex: 100,
      }}>{text}</span>
    </span>
  );
}

window.DSBottomSheet = DSBottomSheet;
window.DSDialog = DSDialog;
window.DSTooltip = DSTooltip;
