/*
 *  MessageBubble.tsx
 *  Zyquo Cloud Web
 *
 *  Author: Simon-Pierre Boucher
 *  Mail: contact@spboucher.ai
 *
 *  One chat turn: user right (accent-subtle) / assistant left (surface +
 *  hairline + provider avatar). Markdown body, collapsible reasoning,
 *  citation chips, attachments, error note, hover metadata + actions
 *  (copy, edit & resend, regenerate, quote, branch, read-aloud, delete),
 *  variant switcher, streaming caret.
 */

import { memo, useState } from 'react'
import { costLabel } from '../features/catalogHelpers'
import { useStore } from '../state/store'
import type { Message } from '../types'
import { Markdown } from './Markdown'
import {
  IconBranch,
  IconCheck,
  IconChevronRight,
  IconCopy,
  IconEdit,
  IconQuote,
  IconRefresh,
  IconSpeaker,
  IconTrash,
  IconX,
  ProviderGlyph,
} from './icons'

function timeLabel(timestamp: number): string {
  return new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}

export const MessageBubble = memo(function MessageBubble({
  conversationID,
  message,
  streaming,
  isLast = false,
  onQuote,
}: {
  conversationID: string
  message: Message
  streaming: boolean
  isLast?: boolean
  onQuote: (text: string) => void
}) {
  const continueGeneration = useStore((s) => s.continueGeneration)
  const regenerate = useStore((s) => s.regenerate)
  const editAndResend = useStore((s) => s.editAndResend)
  const deleteMessage = useStore((s) => s.deleteMessage)
  const branchFrom = useStore((s) => s.branchFrom)
  const setActiveVariant = useStore((s) => s.setActiveVariant)
  const toast = useStore((s) => s.toast)
  const [reasoningOpen, setReasoningOpen] = useState(false)
  const [editing, setEditing] = useState(false)
  const [draft, setDraft] = useState(message.text)
  const [copied, setCopied] = useState(false)

  const isUser = message.role === 'user'
  const variants = message.variants ?? []
  const activeVariant =
    message.activeVariant !== undefined ? variants[message.activeVariant] : undefined
  const shownText = activeVariant?.text ?? message.text
  const shownReasoning = activeVariant?.reasoning ?? message.reasoning
  const shownCitations = activeVariant?.citations ?? message.citations
  const shownUsage = activeVariant?.usage ?? message.usage
  const shownCost = activeVariant?.estimatedCost ?? message.estimatedCost

  const copy = () => {
    void navigator.clipboard.writeText(shownText)
    setCopied(true)
    setTimeout(() => setCopied(false), 1200)
  }

  const readAloud = () => {
    if (!('speechSynthesis' in window)) {
      toast('Speech synthesis not available in this browser', 'error')
      return
    }
    speechSynthesis.cancel()
    speechSynthesis.speak(new SpeechSynthesisUtterance(shownText))
  }

  return (
    <div className={`turn ${isUser ? 'user' : 'assistant'}`}>
      {!isUser && (
        <div className="avatar">
          <ProviderGlyph provider={message.provider ?? 'custom'} size={12} />
        </div>
      )}
      <div className="turn-body">
        <div className="bubble">
          {(message.attachments ?? []).filter((a) => a.kind === 'image').length > 0 && (
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 6 }}>
              {(message.attachments ?? [])
                .filter((a) => a.kind === 'image')
                .map((a) => (
                  <img
                    key={a.id}
                    src={`data:${a.mimeType};base64,${a.data}`}
                    alt={a.fileName}
                    style={{ maxWidth: 160, maxHeight: 120, borderRadius: 6, objectFit: 'cover' }}
                  />
                ))}
            </div>
          )}
          {(message.attachments ?? []).filter((a) => a.kind === 'textFile').length > 0 && (
            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginBottom: 6 }}>
              {(message.attachments ?? [])
                .filter((a) => a.kind === 'textFile')
                .map((a) => (
                  <span key={a.id} className="badge">
                    {a.fileName}
                  </span>
                ))}
            </div>
          )}

          {shownReasoning !== undefined && shownReasoning !== '' && (
            <div className="reasoning-section">
              <button className="reasoning-toggle" onClick={() => setReasoningOpen((o) => !o)}>
                <span className={`chev${reasoningOpen ? ' open' : ''}`}>
                  <IconChevronRight />
                </span>
                {streaming && shownText === '' ? 'Thinking…' : 'Thought process'}
              </button>
              {reasoningOpen && <div className="reasoning-body">{shownReasoning}</div>}
            </div>
          )}

          {editing ? (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <textarea
                className="text-input"
                rows={Math.min(8, Math.max(2, draft.split('\n').length))}
                value={draft}
                onChange={(e) => setDraft(e.target.value)}
                autoFocus
              />
              <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
                <button className="button" onClick={() => setEditing(false)}>
                  Cancel
                </button>
                <button
                  className="button primary"
                  disabled={draft.trim() === ''}
                  onClick={() => {
                    setEditing(false)
                    void editAndResend(conversationID, message.id, draft)
                  }}
                >
                  Resend
                </button>
              </div>
            </div>
          ) : isUser ? (
            shownText
          ) : (
            <>
              <Markdown text={shownText} />
              {streaming && <span className="streaming-caret" />}
            </>
          )}

          {shownCitations && shownCitations.length > 0 && (
            <div className="citations">
              {shownCitations.map((citation) => (
                <a
                  key={citation.index}
                  className="citation-chip"
                  href={citation.url}
                  target="_blank"
                  rel="noreferrer noopener"
                  title={citation.url}
                >
                  <span className="num">{citation.index}</span>
                  <span className="title">
                    {citation.title ?? new URL(citation.url).hostname}
                  </span>
                </a>
              ))}
            </div>
          )}

          {message.errorText && (
            <div className="error-note">
              <span>⚠</span>
              <span>{message.errorText}</span>
            </div>
          )}
        </div>

        <div className={`meta-row${streaming ? ' always' : ''}`}>
          {!isUser && variants.length > 0 && (
            <span className="variant-switch">
              <button
                className="meta-action"
                title="Previous variant"
                onClick={() => {
                  const current = message.activeVariant ?? -1
                  setActiveVariant(conversationID, message.id, Math.max(-1, current - 1) as number)
                }}
              >
                ‹
              </button>
              {(message.activeVariant ?? -1) + 2}/{variants.length + 1}
              <button
                className="meta-action"
                title="Next variant"
                onClick={() => {
                  const current = message.activeVariant ?? -1
                  setActiveVariant(
                    conversationID,
                    message.id,
                    Math.min(variants.length - 1, current + 1)
                  )
                }}
              >
                ›
              </button>
            </span>
          )}
          {!isUser && (
            <>
              <button className="meta-action" title="Copy" onClick={copy}>
                {copied ? <IconCheck size={10} /> : <IconCopy size={10} />}
              </button>
              <button
                className="meta-action"
                title="Regenerate"
                onClick={() => void regenerate(conversationID, message.id)}
              >
                <IconRefresh size={10} />
              </button>
              <button className="meta-action" title="Quote reply" onClick={() => onQuote(shownText)}>
                <IconQuote size={10} />
              </button>
              <button
                className="meta-action"
                title="Branch from here"
                onClick={() => branchFrom(conversationID, message.id)}
              >
                <IconBranch size={10} />
              </button>
              <button className="meta-action" title="Read aloud" onClick={readAloud}>
                <IconSpeaker size={10} />
              </button>
              {isLast && !streaming && shownText !== '' && (
                <button
                  className="meta-action"
                  title="Ask the model to keep going"
                  onClick={() => void continueGeneration(conversationID)}
                >
                  continue
                </button>
              )}
            </>
          )}
          {isUser && (
            <>
              <button className="meta-action" title="Copy" onClick={copy}>
                {copied ? <IconCheck size={10} /> : <IconCopy size={10} />}
              </button>
              <button
                className="meta-action"
                title="Edit & resend"
                onClick={() => {
                  setDraft(message.text)
                  setEditing(true)
                }}
              >
                <IconEdit size={10} />
              </button>
            </>
          )}
          <button
            className="meta-action"
            title="Delete"
            onClick={() => deleteMessage(conversationID, message.id)}
          >
            <IconTrash size={10} />
          </button>
          <span>{timeLabel(message.createdAt)}</span>
          {shownUsage && (
            <span>
              {shownUsage.inputTokens}→{shownUsage.outputTokens} tok
            </span>
          )}
          {shownCost !== undefined && shownCost > 0 && <span>{costLabel(shownCost)}</span>}
          {streaming && <StopHint conversationID={conversationID} />}
        </div>
      </div>
    </div>
  )
})

function StopHint({ conversationID }: { conversationID: string }) {
  const stop = useStore((s) => s.stop)
  const streaming = useStore((s) => s.streaming[conversationID])
  if (!streaming) return null
  const elapsed = (Date.now() - streaming.startedAt) / 1000
  const tps = elapsed > 0 ? Math.round(streaming.outputChars / 4 / elapsed) : 0
  return (
    <>
      <span>
        {elapsed.toFixed(0)}s{tps > 0 ? ` · ~${tps} tok/s` : ''}
      </span>
      <button className="meta-action" onClick={() => stop(conversationID)}>
        <IconX size={9} /> stop
      </button>
    </>
  )
}
