// DSChat.jsx — Meridian chat surface primitives
//
//   DSMessageBubble — assistant / user / system variants with proper
//                     alignment and bubble shape
//   DSComposer      — input field + attachments slot + send + optional voice FAB
//   DSThinkingDots  — typing indicator (three dots cascading; reduced-motion safe)
//   DSAxisAvatar    — Axis brand mark (ink-blue square + white arrow)
//   DSUserAvatar    — user identicon (initials) or photo
//
// DSAxisAvatar is kept as a back-compat alias for existing screens.

// ── DSAxisAvatar ─────────────────────────────────────────────────
function DSAxisAvatar({ size = 28, radius }) {
  const r = radius ?? Math.max(6, size * 0.28);
  return (
    <div aria-label="Axis" role="img" style={{
      width: size, height: size, borderRadius: r,
      background: M.axis, color: '#fff', flexShrink: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <svg width={size * 0.5} height={size * 0.5} viewBox="0 0 24 24" fill="none">
        <path d="M4 12h14M13 6l6 6-6 6"
              stroke="#fff" strokeWidth="2.4"
              strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    </div>
  );
}

// ── DSUserAvatar ─────────────────────────────────────────────────
function DSUserAvatar({ initials, photoUrl, size = 28, radius, color = M.ink }) {
  const r = radius ?? Math.max(6, size * 0.28);
  const [imgFailed, setImgFailed] = React.useState(false);
  const showPhoto = photoUrl && !imgFailed;
  // Initials derivation:
  //   "Eleanor Mae" → "EM"  (whitespace-separated → first letter of each word)
  //   "EM"          → "EM"  (already a glyph pair → keep as-is)
  //   "Eleanor"     → "EL"
  const trimmed = String(initials || '').trim();
  const text = (
    /\s/.test(trimmed)
      ? trimmed.split(/\s+/).map(s => s[0]).join('').slice(0, 2)
      : trimmed.slice(0, 2)
  ).toUpperCase() || '?';

  return (
    <div aria-label={initials ? `${initials} avatar` : 'User'} role="img" style={{
      width: size, height: size, borderRadius: r,
      background: showPhoto ? M.ruleSoft : color,
      color: '#fff', flexShrink: 0,
      overflow: 'hidden',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      font: `500 ${Math.round(size * 0.42)}px/1 ${M.sans}`,
    }}>
      {showPhoto
        ? <img src={photoUrl} alt="" onError={() => setImgFailed(true)}
               style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
        : <span>{text}</span>}
    </div>
  );
}

// ── DSThinkingDots ───────────────────────────────────────────────
// Three dots cascading. Respects prefers-reduced-motion: in that case,
// renders three static dots at uniform opacity instead.
function DSThinkingDots({ color = M.inkMuted, size = 6, gap = 4 }) {
  const [reduce, setReduce] = React.useState(false);
  React.useEffect(() => {
    const mql = window.matchMedia('(prefers-reduced-motion: reduce)');
    setReduce(mql.matches);
    const fn = (e) => setReduce(e.matches);
    mql.addEventListener?.('change', fn);
    return () => mql.removeEventListener?.('change', fn);
  }, []);

  return (
    <span aria-label="Thinking" role="status" style={{
      display: 'inline-flex', alignItems: 'center', gap,
      padding: '6px 2px',
    }}>
      <style>{`
        @keyframes meridian-thinking-dot {
          0%, 80%, 100% { opacity: .25; transform: translateY(0); }
          40%           { opacity: 1;    transform: translateY(-2px); }
        }
      `}</style>
      {[0, 1, 2].map(i => (
        <span key={i} aria-hidden="true" style={{
          width: size, height: size, borderRadius: '50%',
          background: color,
          opacity: reduce ? 0.55 : undefined,
          animation: reduce ? 'none' : `meridian-thinking-dot 1.2s ${i * 0.15}s infinite ease-in-out`,
        }} />
      ))}
    </span>
  );
}

// ── DSMessageBubble ──────────────────────────────────────────────
// Variants:
//   assistant — left, surface bg, 1px rule, 14-radius w/ 4 on top-left
//   user      — right, axis-blue bg, white text, 14-radius w/ 4 on top-right
//   system    — centered, muted small caps; for time dividers, "Tool used", etc.
function DSMessageBubble({
  sender = 'assistant',
  state = 'idle',          // 'idle' | 'thinking' | 'streaming'
  children,
  text,                    // shorthand for `children`
  showAvatar = true,       // assistant only — set false to continue a sequence
  chips,                   // [{ variant, label }] — DSChip family
  actions,                 // [{ label, variant, onClick }]
  continued = false,       // assistant — adds "continued…" marker
  timestamp,               // optional metadata under bubble
  style,
}) {
  const body = children ?? text;

  if (sender === 'system') {
    return (
      <div role="separator" style={{
        display: 'flex', justifyContent: 'center', padding: '6px 0',
        ...style,
      }}>
        <span style={{
          font: `500 10px/1 ${M.sans}`,
          color: M.inkFaint,
          letterSpacing: '0.1em', textTransform: 'uppercase',
        }}>{body}</span>
      </div>
    );
  }

  const isAssistant = sender === 'assistant';
  const align = isAssistant ? 'flex-start' : 'flex-end';
  const bubbleBg = isAssistant ? M.surface : M.axis;
  const bubbleColor = isAssistant ? M.ink : '#fff';
  const bubbleBorder = isAssistant ? `1px solid ${M.rule}` : '1px solid transparent';
  const tuckCorner = isAssistant
    ? { borderTopLeftRadius: 4 }
    : { borderTopRightRadius: 4 };

  return (
    <div style={{
      display: 'flex',
      flexDirection: isAssistant ? 'row' : 'row-reverse',
      gap: 10,
      alignItems: 'flex-start',
      ...style,
    }}>
      {isAssistant && showAvatar && <DSAxisAvatar size={28} radius={8} />}
      {/* Spacer for when avatar is hidden (sequence continuation) */}
      {isAssistant && !showAvatar && <div style={{ width: 28, flexShrink: 0 }} aria-hidden="true" />}

      <div style={{
        maxWidth: '78%',
        display: 'flex', flexDirection: 'column', alignItems: align, gap: 6,
      }}>
        <div style={{
          background: bubbleBg, color: bubbleColor,
          border: bubbleBorder,
          borderRadius: 14, ...tuckCorner,
          padding: '11px 13px',
          font: `400 14px/1.5 ${M.sans}`,
        }}>
          {state === 'thinking'
            ? <DSThinkingDots color={isAssistant ? M.inkMuted : '#fff'} />
            : body}
          {state === 'streaming' && (
            <span aria-hidden="true" style={{
              display: 'inline-block',
              width: 2, height: '1em', marginLeft: 2,
              background: isAssistant ? M.ink : '#fff',
              verticalAlign: '-2px',
              animation: 'meridian-cursor 1s infinite step-start',
            }} />
          )}
          {state === 'streaming' && (
            <style>{`@keyframes meridian-cursor { 50% { opacity: 0 } }`}</style>
          )}
          {continued && (
            <div style={{
              font: `400 12px/1 ${M.sans}`,
              color: isAssistant ? M.inkFaint : 'rgba(255,255,255,0.7)',
              fontStyle: 'italic', marginTop: 8,
            }}>continued…</div>
          )}
          {chips && chips.length > 0 && (
            <div style={{ display: 'flex', gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
              {chips.map((c, i) => (
                <DSChip key={i} variant={c.variant} dot={c.dot}
                       style={{ padding: '4px 8px', fontSize: 11, ...c.style }}>
                  {c.label}
                </DSChip>
              ))}
            </div>
          )}
          {actions && actions.length > 0 && (
            <div style={{ display: 'flex', gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
              {actions.map((a, i) => (
                <DSButton key={i} size="sm" variant={a.variant || (i === 0 ? 'primary' : 'secondary')}
                         onClick={a.onClick}
                         style={{ borderRadius: 8, padding: '6px 10px', fontSize: 12 }}>
                  {a.label}
                </DSButton>
              ))}
            </div>
          )}
        </div>
        {timestamp && (
          <div style={{
            font: `400 11px/1 ${M.sans}`, color: M.inkFaint,
            padding: '0 2px',
          }}>{timestamp}</div>
        )}
      </div>
    </div>
  );
}

// ── DSComposer ───────────────────────────────────────────────────
// Pill-shaped composer with:
//   - leading attachments slot (e.g. + button, file chips, audio meter)
//   - input field
//   - optional voice FAB slot (mic toggle)
//   - trailing send button (disabled until input non-empty unless an
//     attachment is present)
function DSComposer({
  value,
  onChange,
  onSubmit,
  placeholder = 'Ask Axis…',
  attachmentsSlot,
  voiceSlot,
  disabled = false,
  hasAttachments = false,    // forces send button to enable even with empty text
  style,
}) {
  const canSend = !disabled && (value?.trim().length > 0 || hasAttachments);

  const send = () => {
    if (!canSend) return;
    onSubmit?.(value?.trim() || '');
  };

  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 8,
      background: M.surface,
      border: `1px solid ${M.rule}`,
      borderRadius: 999,
      padding: '6px 6px 6px 12px',
      opacity: disabled ? 0.6 : 1,
      ...style,
    }}>
      {attachmentsSlot && (
        <div style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}>
          {attachmentsSlot}
        </div>
      )}
      <input
        value={value || ''}
        onChange={(e) => onChange?.(e.target.value)}
        onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } }}
        placeholder={placeholder}
        disabled={disabled}
        style={{
          flex: 1, minWidth: 0,
          border: 'none', outline: 'none',
          background: 'transparent',
          font: `400 15px/1.2 ${M.sans}`, color: M.ink,
          padding: '4px 6px',
        }}
      />
      {voiceSlot && (
        <div style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}>
          {voiceSlot}
        </div>
      )}
      <button
        type="button"
        onClick={send}
        disabled={!canSend}
        aria-label="Send"
        style={{
          width: 32, height: 32, borderRadius: 999,
          border: 'none',
          background: canSend ? M.axis : M.inkFaint,
          color: '#fff',
          cursor: canSend ? 'pointer' : 'default',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
          transition: 'background 120ms cubic-bezier(.2,.8,.2,1)',
        }}>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
          <path d="M4 12h14M13 6l6 6-6 6" stroke="#fff" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </button>
    </div>
  );
}

Object.assign(window, {
  DSMessageBubble, DSComposer, DSThinkingDots, DSAxisAvatar, DSUserAvatar,
  // Back-compat alias — existing screens import DSAxisAvatar
  DSAxisAvatar: DSAxisAvatar,
});
