// DSToast.jsx — Meridian transient confirmation toast
//
// Bottom-anchored, dark pill, ephemeral. The counterpart to DSAlert
// (banner, light, persistent until dismissed). DSToast is the pill —
// dark tone, single action slot, auto-dismisses. The two complement
// each other; they don't overlap.
//
// Used for quick-log confirmations on Today, voice-note extraction
// summaries, block-activation receipts — moments where the user just
// did something and we want a calm, peripheral acknowledgement
// without taking over the surface.
//
// Props:
//   title     String         required   primary copy ("Sleep logged")
//   value     String?        optional   sub-line ("7.5 hrs") — mono / tabular
//   icon      Node?          optional   leading icon (24px viewBox, currentColor)
//   onUndo    () => void     optional   when set, renders inline "Undo" action;
//                                       tapping invokes the callback and
//                                       dismisses the toast immediately
//   duration  Number         default 5000ms — auto-dismiss timer
//   onDismiss () => void     optional   fires after exit animation completes
//   style     Object?        optional   override / position overrides
//
// Behavior:
//   - Fade + slide-up on enter, fade + slide-down on exit (220ms each)
//   - Tapping the body extends the duration by 2s (clamped to 15s total)
//   - Tapping Undo dismisses immediately and invokes the callback
//   - prefers-reduced-motion: slide is dropped, fade only
//
// Accessibility:
//   - role="status" aria-live="polite" — screen readers announce
//     without interrupting current speech
//   - Undo is a real <button> with aria-label so assistive tech
//     exposes a semantic action
//
// Positioning:
//   DSToast uses position:fixed at the bottom of the viewport with
//   safe-area-inset-bottom padding. It can be contained inside an
//   iOS frame or a device mock by giving an ancestor a CSS containing
//   block (e.g. `transform: translateZ(0)`).

const DSToast_DURATION_MAX = 15000;
const DSToast_EXTEND_STEP  = 2000;
const DSToast_ANIM_MS      = 220;

function DSToast({
  title,
  value,
  icon,
  onUndo,
  duration = 5000,
  onDismiss,
  style,
}) {
  const [phase, setPhase] = React.useState('enter');  // 'enter' | 'visible' | 'exit'
  const [extension, setExtension] = React.useState(0);
  const mountedAtRef  = React.useRef(Date.now());
  const dismissTimerRef = React.useRef(null);

  const reduceMotion = React.useMemo(() => {
    if (typeof window === 'undefined' || !window.matchMedia) return false;
    return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  }, []);

  // Kick the entry transition on the next frame so transition fires.
  React.useEffect(() => {
    if (phase !== 'enter') return;
    const t = setTimeout(() => setPhase('visible'), 16);
    return () => clearTimeout(t);
  }, [phase]);

  // (Re)schedule auto-dismiss whenever the effective duration changes.
  React.useEffect(() => {
    if (phase === 'exit') return;
    clearTimeout(dismissTimerRef.current);
    const elapsed = Date.now() - mountedAtRef.current;
    const total   = Math.min(DSToast_DURATION_MAX, duration + extension);
    const remaining = Math.max(DSToast_ANIM_MS, total - elapsed);
    dismissTimerRef.current = setTimeout(() => setPhase('exit'), remaining);
    return () => clearTimeout(dismissTimerRef.current);
  }, [extension, phase, duration]);

  // After the exit animation, hand control back to the parent.
  React.useEffect(() => {
    if (phase !== 'exit') return;
    const t = setTimeout(() => { onDismiss && onDismiss(); }, DSToast_ANIM_MS);
    return () => clearTimeout(t);
  }, [phase, onDismiss]);

  const extendIfPossible = () => {
    const cap = DSToast_DURATION_MAX - duration;
    const next = Math.min(cap, extension + DSToast_EXTEND_STEP);
    if (next > extension) setExtension(next);
  };

  const handleBodyClick = () => {
    if (phase === 'exit') return;
    extendIfPossible();
  };

  const handleUndoClick = (e) => {
    e.stopPropagation();
    if (phase === 'exit') return;
    onUndo && onUndo();
    setPhase('exit');
  };

  const visible = phase === 'visible';
  const slideOffset = reduceMotion ? '0' : (phase === 'exit' ? '8px' : '12px');
  const transform = visible ? 'translateY(0)' : `translateY(${slideOffset})`;
  const opacity   = visible ? 1 : 0;

  return (
    <div
      role="status"
      aria-live="polite"
      aria-atomic="true"
      style={{
        position: 'fixed',
        left: 0, right: 0,
        bottom: 'max(16px, env(safe-area-inset-bottom, 16px))',
        zIndex: 9999,
        display: 'flex',
        justifyContent: 'center',
        padding: '0 16px',
        pointerEvents: 'none',
        ...style,
      }}
    >
      <div
        style={{
          pointerEvents: 'auto',
          width: '100%',
          maxWidth: 480,
          display: 'flex',
          alignItems: 'stretch',
          background: M.ink,
          color: '#FFFFFF',
          borderRadius: 16,
          boxShadow: '0 16px 40px rgba(14,18,32,0.30), 0 2px 6px rgba(14,18,32,0.20)',
          opacity,
          transform,
          transition: `opacity ${DSToast_ANIM_MS}ms cubic-bezier(.2,.8,.2,1), transform ${DSToast_ANIM_MS}ms cubic-bezier(.2,.8,.2,1)`,
          overflow: 'hidden',
        }}
      >
        {/* Body — tappable to extend */}
        <button
          type="button"
          onClick={handleBodyClick}
          style={{
            all: 'unset',
            cursor: 'pointer',
            flex: 1,
            minWidth: 0,
            display: 'flex',
            alignItems: 'center',
            gap: 12,
            padding: '14px 16px',
            boxSizing: 'border-box',
          }}
        >
          {icon && (
            <span aria-hidden="true" style={{
              flexShrink: 0,
              width: 20, height: 20,
              display: 'inline-flex',
              alignItems: 'center',
              justifyContent: 'center',
              color: '#FFFFFF',
            }}>{icon}</span>
          )}
          <span style={{
            flex: 1,
            minWidth: 0,
            display: 'inline-flex',
            alignItems: 'baseline',
            gap: 8,
            flexWrap: 'wrap',
          }}>
            <span style={{
              font: `500 14px/1.3 ${M.sans}`,
              color: '#FFFFFF',
              letterSpacing: '-0.005em',
            }}>{title}</span>
            {value && (
              <span style={{
                font: `400 13px/1.3 ${M.mono}`,
                color: 'rgba(255,255,255,0.62)',
                fontVariantNumeric: 'tabular-nums',
              }}>{value}</span>
            )}
          </span>
        </button>

        {/* Undo action — semantic <button> */}
        {onUndo && (
          <>
            <span aria-hidden="true" style={{
              width: 1,
              background: 'rgba(255,255,255,0.10)',
              flexShrink: 0,
              margin: '8px 0',
            }} />
            <button
              type="button"
              aria-label="Undo last action"
              onClick={handleUndoClick}
              style={{
                all: 'unset',
                cursor: 'pointer',
                padding: '0 18px',
                display: 'inline-flex',
                alignItems: 'center',
                justifyContent: 'center',
                font: `500 14px/1 ${M.sans}`,
                letterSpacing: '-0.005em',
                color: '#B8C5FF',  /* lightened Axis blue, AA on M.ink */
                flexShrink: 0,
              }}
              onMouseDown={(e)=>{ e.currentTarget.style.color='#FFFFFF'; }}
              onMouseUp  ={(e)=>{ e.currentTarget.style.color='#B8C5FF'; }}
              onMouseLeave={(e)=>{ e.currentTarget.style.color='#B8C5FF'; }}
            >Undo</button>
          </>
        )}
      </div>
    </div>
  );
}

Object.assign(window, {
  DSToast,
  DSToast_DURATION_MAX,
  DSToast_EXTEND_STEP,
  DSToast_ANIM_MS,
});
