import type { CSSProperties, ReactNode } from 'react';
import type { Message } from './types.js';

export interface MessageBubbleProps {
  message: Message;
  /**
   * Optional action buttons rendered in the footer (copy / retry /
   * feedback). Receives the message so each handler can be wired
   * statelessly.
   */
  actions?: (message: Message) => ReactNode;
  /** Override the rendered role label (default: capitalized role). */
  roleLabel?: string;
  /** Tailwind-friendly className escape hatch. */
  className?: string;
  /** Inline style escape hatch. */
  style?: CSSProperties;
}

/**
 * Atom: a single chat message bubble. Variants by role (user / assistant /
 * system / tool). Honors `--bl-*` design tokens so it inherits whatever
 * scheme + density is active on the surrounding `<html>`.
 *
 * The "thinking" indicator (three pulsing dots) appears for assistant
 * messages while `isStreaming` is true and content is empty — i.e. the
 * window between request-sent and first-token-received.
 */
export function MessageBubble({
  message,
  actions,
  roleLabel,
  className,
  style,
}: MessageBubbleProps) {
  const isUser = message.role === 'user';
  const isAssistant = message.role === 'assistant';
  const isSystem = message.role === 'system';
  const isTool = message.role === 'tool';

  const thinking = isAssistant && message.isStreaming && !message.content;

  // Variant colors via design tokens — tone shifts so the eye can
  // separate user from assistant without relying on alignment alone.
  const variantStyle: CSSProperties = isUser
    ? {
        background: 'var(--bl-accent-muted, rgba(99,102,241,0.12))',
        color: 'var(--bl-text-primary, inherit)',
        marginInlineStart: 'auto',
        borderInlineEnd: '1px solid var(--bl-accent, #6366f1)',
      }
    : isAssistant
      ? {
          background: 'var(--bl-surface-card, #fff)',
          color: 'var(--bl-text-primary, inherit)',
          border: '1px solid var(--bl-border, rgba(0,0,0,0.08))',
        }
      : isSystem
        ? {
            background: 'transparent',
            color: 'var(--bl-text-tertiary, #888)',
            fontStyle: 'italic',
            border: '1px dashed var(--bl-border-subtle, rgba(0,0,0,0.06))',
          }
        : /* tool */ {
            background: 'var(--bl-surface-muted, #f6f6f6)',
            color: 'var(--bl-text-secondary, #555)',
            fontFamily: 'var(--bl-font-mono, ui-monospace, monospace)',
            border: '1px solid var(--bl-border-subtle, rgba(0,0,0,0.06))',
          };

  return (
    <article
      data-testid={`bl-message-${message.id}`}
      data-role={message.role}
      data-streaming={message.isStreaming ? 'true' : 'false'}
      className={className}
      style={{
        maxWidth: isSystem || isTool ? '100%' : '85%',
        padding: 'var(--bl-space-3, 12px) var(--bl-space-4, 16px)',
        borderRadius: 'var(--bl-radius-card, 12px)',
        fontSize: '0.95rem',
        lineHeight: 1.5,
        whiteSpace: 'pre-wrap',
        wordBreak: 'break-word',
        ...variantStyle,
        ...style,
      }}
    >
      <header
        style={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'space-between',
          fontSize: '0.75rem',
          color: 'var(--bl-text-tertiary, #888)',
          marginBottom: message.content || thinking ? 'var(--bl-space-1, 4px)' : 0,
          textTransform: 'uppercase',
          letterSpacing: '0.04em',
        }}
      >
        <span>{roleLabel ?? capitalize(message.role)}</span>
        {message.createdAt && (
          <time dateTime={message.createdAt.toISOString()}>
            {formatTime(message.createdAt)}
          </time>
        )}
      </header>

      {thinking ? <ThinkingDots /> : <div>{message.content}</div>}

      {actions && (
        <footer
          style={{
            marginTop: 'var(--bl-space-2, 8px)',
            display: 'flex',
            gap: 'var(--bl-space-2, 8px)',
          }}
        >
          {actions(message)}
        </footer>
      )}
    </article>
  );
}

function capitalize(s: string): string {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function formatTime(d: Date): string {
  // 24-h HH:MM — locale-agnostic so visual-regression snapshots stay stable.
  const h = String(d.getHours()).padStart(2, '0');
  const m = String(d.getMinutes()).padStart(2, '0');
  return `${h}:${m}`;
}

function ThinkingDots() {
  // Three blinking dots; sequenced with CSS-only animation-delay so we
  // don't depend on @bytelyst/motion (which lands in Wave 4).
  const dot: CSSProperties = {
    display: 'inline-block',
    width: 6,
    height: 6,
    margin: '0 2px',
    borderRadius: '50%',
    background: 'currentColor',
    opacity: 0.4,
    animation: 'bl-ai-pulse 1.2s infinite ease-in-out',
  };
  return (
    <span
      aria-label="Assistant is typing"
      role="status"
      style={{ display: 'inline-flex', alignItems: 'center', padding: '2px 0' }}
    >
      <span style={{ ...dot, animationDelay: '0ms' }} />
      <span style={{ ...dot, animationDelay: '180ms' }} />
      <span style={{ ...dot, animationDelay: '360ms' }} />
      <style>{`@keyframes bl-ai-pulse{0%,80%,100%{opacity:.25;transform:scale(0.85)}40%{opacity:1;transform:scale(1)}}`}</style>
    </span>
  );
}
