<message-bubble>
  <div class={'message ' + (props.message?.is_ai ? 'ai-message' : '') + (props.isOwn ? ' own-message' : '')}>
    <div class="message-avatar-col">
      <div class={'message-avatar ' + (props.message?.is_ai ? 'ai-avatar' : '')}>
        <svg if={props.message?.is_ai} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <rect x="4" y="8" width="16" height="12" rx="2"/>
          <line x1="12" y1="2" x2="12" y2="8"/>
          <circle cx="12" cy="2" r="1" fill="currentColor"/>
          <circle cx="9" cy="14" r="1.5" fill="currentColor"/>
          <circle cx="15" cy="14" r="1.5" fill="currentColor"/>
        </svg>
        <img if={!props.message?.is_ai}
             src={getMessageAvatar(props.message)}
             alt={props.message?.sender_name}
             width="32"
             height="32"
             class="avatar-img"
             loading="lazy"
        />
      </div>
    </div>
    <div class="message-body">
      <div class={'message-header ' + (props.isOwn ? 'own' : '')}>
        <span class="sender-name">{props.message?.sender_name}</span>
        <span class="message-time" title={fullTimestamp(props.message?.created_at)}>{formatTime(props.message?.created_at)}</span>
        <span if={props.message?.hash} class="message-hash" title={props.message.hash}>{props.message.hash.slice(0, 7)}</span>
      </div>
      <div if={hasToolResults()} class="tool-results-section">
        <div each={tr in getToolResults()} class="tool-result-item">
          <button class="tool-result-toggle" onclick={toggleToolResult}>
            <span class="tool-result-icon">{isSearchTool(tr.tool) ? '🔍' : tr.tool === 'web_fetch' ? '🌐' : '⚙️'}</span>
            <span class="tool-result-label">{isSearchTool(tr.tool) ? 'Search' : tr.tool === 'web_fetch' ? 'Fetched' : tr.tool}: {tr.input}</span>
            <span class="tool-result-arrow">▼</span>
          </button>
          <div class="tool-result-body collapsed">
            <pre class="tool-result-content">{tr.result}</pre>
          </div>
        </div>
      </div>
      <div if={props.message?.image_url} class="message-image-wrap">
        <img src={props.message.image_url} alt="Attached image" class="message-image" loading="lazy" onclick={openImageFullscreen} />
      </div>
      <div if={props.isStreaming} class="message-content streaming-content">{props.message?.content}<span class="streaming-cursor">▌</span></div>
      <div if={!props.isStreaming} class="message-content markdown-content"></div>
      <div if={!props.isStreaming} class="message-actions-bar">
        <button class="msg-action-btn" onclick={copyFullMessage} title="Copy message">
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
          </svg>
        </button>
        <button if={props.message?.hash} class="msg-action-btn" onclick={copyMessageLink} title="Copy link to message">
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
            <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
          </svg>
        </button>
        <template if={props.message?.is_ai && props.message?.ai_meta}>
          <span class="ai-stat-item model">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a4 4 0 0 0-4 4v2H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h-2V6a4 4 0 0 0-4-4z"/></svg>
            {formatModel(props.message.ai_meta.model)}
          </span>
          <span class="ai-stat-item" title="Generation speed">
            ⚡ {calcSpeed(props.message.ai_meta)} tok/sec
          </span>
          <span class="ai-stat-item" title="Completion tokens">
            🎯 {props.message.ai_meta.completion_tokens} tokens
          </span>
          <span class="ai-stat-item" title="Response time">
            ⏱ {(props.message.ai_meta.response_ms / 1000).toFixed(1)}s
          </span>
        </template>
      </div>
    </div>
  </div>

  <style>
    .message {
      display: flex;
      gap: var(--space-sm);
      max-width: 80%;
      animation: fadeIn 0.2s ease;
    }

    .own-message {
      margin-left: auto;
      flex-direction: row-reverse;
    }

    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(4px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .message-avatar-col {
      flex-shrink: 0;
      padding-top: 2px;
    }

    .message-avatar {
      width: 32px;
      height: 32px;
      border-radius: var(--radius-full);
      background: var(--bg-elevated);
      display: flex;
      align-items: center;
      justify-content: center;
      font-weight: 600;
      font-size: var(--text-xs);
      color: var(--text-secondary);
      overflow: hidden;
    }

    .avatar-img {
      width: 100%;
      height: 100%;
      border-radius: var(--radius-full);
      object-fit: cover;
    }

    .ai-avatar {
      background: var(--accent);
      color: white;
    }

    .message-body {
      min-width: 0;
    }

    .message-header {
      display: flex;
      align-items: baseline;
      gap: var(--space-sm);
      margin-bottom: 2px;
    }

    .message-header.own {
      justify-content: flex-end;
      flex-direction: row-reverse;
    }

    .sender-name {
      font-size: var(--text-sm);
      font-weight: 600;
      color: var(--text-primary);
    }

    .ai-message .sender-name {
      color: var(--accent);
    }

    .message-time {
      font-size: var(--text-xs);
      color: var(--text-muted);
    }

    .message-hash {
      font-family: var(--font-mono, 'SF Mono', 'Fira Code', monospace);
      font-size: 10px;
      color: var(--text-muted);
      opacity: 0.5;
      cursor: default;
      user-select: all;
      transition: opacity var(--transition-fast);
    }

    .message-hash:hover {
      opacity: 1;
    }

    .message-content {
      padding: var(--space-sm) var(--space-md);
      border-radius: var(--radius-lg);
      font-size: var(--text-sm);
      line-height: 1.6;
      word-wrap: break-word;
    }

    .message:not(.own-message):not(.ai-message) .message-content {
      background: var(--bg-tertiary);
      border-top-left-radius: var(--radius-sm);
    }

    .own-message .message-content {
      background: var(--accent);
      color: white;
      border-top-right-radius: var(--radius-sm);
    }

    .own-message .message-content :global(code) {
      background: rgba(255, 255, 255, 0.15);
    }

    .own-message .message-content :global(a) {
      color: white;
      text-decoration: underline;
    }

    .ai-message .message-content {
      background: var(--ai-bg);
      border: 1px solid var(--ai-border);
      border-top-left-radius: var(--radius-sm);
    }

    .tool-results-section {
      margin-bottom: 6px;
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

    .tool-result-item {
      border-radius: var(--radius-md);
      overflow: hidden;
    }

    .tool-result-toggle {
      display: flex;
      align-items: center;
      gap: 6px;
      width: 100%;
      padding: 5px 10px;
      background: var(--bg-tertiary);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      color: var(--text-secondary);
      font-size: 12px;
      cursor: pointer;
      transition: background var(--transition-fast);
      text-align: left;
    }

    .tool-result-toggle:hover {
      background: var(--bg-hover);
    }

    .tool-result-toggle.open {
      border-bottom-left-radius: 0;
      border-bottom-right-radius: 0;
    }

    .tool-result-icon {
      flex-shrink: 0;
    }

    .tool-result-label {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      min-width: 0;
    }

    .tool-result-arrow {
      flex-shrink: 0;
      font-size: 10px;
      transition: transform 0.2s;
    }

    .tool-result-toggle.open .tool-result-arrow {
      transform: rotate(180deg);
    }

    .tool-result-body {
      border: 1px solid var(--border);
      border-top: none;
      border-bottom-left-radius: var(--radius-md);
      border-bottom-right-radius: var(--radius-md);
      background: var(--bg-primary);
    }

    .tool-result-body.collapsed {
      display: none;
    }

    .tool-result-content {
      margin: 0;
      padding: 10px 12px;
      font-size: 11px;
      font-family: var(--font-mono, 'SF Mono', 'Fira Code', monospace);
      line-height: 1.5;
      color: var(--text-secondary);
      white-space: pre-wrap;
      word-break: break-word;
      max-height: 300px;
      overflow-y: auto;
    }

    .message-actions-bar {
      display: flex;
      align-items: center;
      gap: var(--space-sm);
      margin-top: 4px;
      padding: 4px var(--space-sm);
      font-size: 11px;
      color: var(--text-muted);
      flex-wrap: wrap;
      opacity: 0;
      transition: opacity var(--transition-fast);
    }

    .message:hover .message-actions-bar,
    .message-actions-bar:focus-within {
      opacity: 1;
    }

    /* Always show for AI messages with stats */
    .ai-message .message-actions-bar {
      opacity: 1;
    }

    .msg-action-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background: none;
      color: var(--text-muted);
      padding: 3px;
      border-radius: var(--radius-sm);
      line-height: 1;
    }

    .msg-action-btn:hover {
      background: var(--bg-hover);
      color: var(--text-primary);
    }

    .msg-action-btn.copied {
      color: var(--success);
    }

    .ai-stat-item {
      display: inline-flex;
      align-items: center;
      gap: 3px;
      white-space: nowrap;
    }

    .ai-stat-item.model {
      color: var(--accent);
      font-weight: 500;
    }

    .message-image-wrap {
      margin-bottom: 4px;
      padding: 4px;
      border-radius: var(--radius-md);
      overflow: hidden;
    }

    .message-image {
      max-width: 320px;
      max-height: 280px;
      border-radius: var(--radius-md);
      cursor: pointer;
      display: block;
      object-fit: contain;
      transition: opacity 0.15s;
    }

    .message-image:hover {
      opacity: 0.9;
    }

    .streaming-content {
      white-space: pre-wrap;
      word-wrap: break-word;
    }

    .streaming-cursor {
      animation: cursor-blink 0.8s step-end infinite;
      color: var(--accent);
      font-weight: 300;
    }

    @keyframes cursor-blink {
      0%, 100% { opacity: 1; }
      50% { opacity: 0; }
    }
  </style>

  <script>
    import { renderMarkdown } from '../services/markdown.js'
    import { avatarFromHash } from '../services/avatar.js'

    export default {
      /** Prefer custom avatar_url, fall back to Gravatar hash */
      getMessageAvatar(msg) {
        if (msg?.avatar_url) return msg.avatar_url
        return avatarFromHash(msg?.avatar_hash, 32)
      },

      onMounted() {
        this.renderContent()
      },

      onUpdated() {
        this.renderContent()
      },

      renderContent() {
        if (this.props.isStreaming) return // Don't markdown-render while streaming
        const el = this.$('.message-content.markdown-content')
        if (el && this.props.message?.content) {
          // Riot renders the container, then markdown-it fills in the trusted HTML output.
          el.innerHTML = renderMarkdown(this.props.message.content)
          // Inject copy buttons into code blocks
          el.querySelectorAll('pre').forEach((pre) => {
            if (pre.querySelector('.code-copy-btn')) return
            const btn = document.createElement('button')
            btn.className = 'code-copy-btn'
            btn.textContent = 'Copy'
            btn.addEventListener('click', () => {
              const code = pre.querySelector('code')
              const text = code ? code.textContent : pre.textContent
              navigator.clipboard.writeText(text).then(() => {
                btn.textContent = 'Copied!'
                btn.classList.add('copied')
                setTimeout(() => {
                  btn.textContent = 'Copy'
                  btn.classList.remove('copied')
                }, 2000)
              })
            })
            pre.appendChild(btn)
          })
        }
      },

      formatTime(dateStr) {
        if (!dateStr) return ''
        const date = new Date(dateStr)
        return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
      },

      fullTimestamp(dateStr) {
        if (!dateStr) return ''
        const date = new Date(dateStr)
        const human = date.toLocaleString([], { dateStyle: 'full', timeStyle: 'long' })
        const unix = Math.floor(date.getTime() / 1000)
        return `${human}\nUnix: ${unix}`
      },

      formatModel(model) {
        if (!model) return 'unknown'
        // "openai/gpt-4o" → "gpt-4o", "anthropic/claude-3.5-sonnet" → "claude-3.5-sonnet"
        const parts = model.split('/')
        return parts.length > 1 ? parts[parts.length - 1] : model
      },

      calcSpeed(meta) {
        if (!meta || !meta.completion_tokens || !meta.response_ms) return '–'
        const seconds = meta.response_ms / 1000
        if (seconds === 0) return '–'
        return (meta.completion_tokens / seconds).toFixed(1)
      },

      hasToolResults() {
        const tr = this.props.message?.ai_meta?.tool_results
        return tr && tr.length > 0
      },

      getToolResults() {
        return this.props.message?.ai_meta?.tool_results || []
      },

      isSearchTool(toolName) {
        return toolName === 'web_search' || toolName === 'brave_search'
      },

      toggleToolResult(e) {
        const toggle = e.currentTarget
        const body = toggle.nextElementSibling
        const isOpen = toggle.classList.contains('open')
        // Keep the collapse logic local to each item so tool results stay independent.
        if (isOpen) {
          toggle.classList.remove('open')
          body.classList.add('collapsed')
        } else {
          toggle.classList.add('open')
          body.classList.remove('collapsed')
        }
      },

      openImageFullscreen(e) {
        const url = e.target.src
        window.open(url, '_blank')
      },

      copyFullMessage(e) {
        e.preventDefault()
        e.stopPropagation()
        const content = this.props.message?.content
        if (!content) return
        const btn = e.currentTarget
        navigator.clipboard.writeText(content).then(() => {
          btn.classList.add('copied')
          btn.title = 'Copied!'
          setTimeout(() => {
            btn.classList.remove('copied')
            btn.title = 'Copy message'
          }, 2000)
        })
      },

      copyMessageLink(e) {
        e.preventDefault()
        e.stopPropagation()
        const hash = this.props.message?.hash
        if (!hash) return
        const roomId = this.props.message?.room_id
        if (!roomId) return
        // Include room id in the fragment so shared links can open the room directly.
        const url = `${window.location.origin}${window.location.pathname}#${roomId}/${hash}`
        const btn = e.currentTarget
        navigator.clipboard.writeText(url).then(() => {
          btn.classList.add('copied')
          btn.title = 'Link copied!'
          setTimeout(() => {
            btn.classList.remove('copied')
            btn.title = 'Copy link to message'
          }, 2000)
        })
      },
    }
  </script>
</message-bubble>
