// RFCDiscussionPanel.jsx — v0.5.0's PR-less per-RFC discussion surface.
//
// Roadmap item #3: an RFC's main view now has a discussion surface
// distinct from PR comments and from branch chat. The substrate is the
// existing threads/thread_messages tables — rows with
// `threads.branch_name IS NULL` scope to "the RFC, no branch yet."
//
// Reused as the right-column panel on `branchParam === 'main'`. Branch
// chat (ChatPanel.jsx) keeps its existing role for branch-scoped work,
// including PRs. Contribution remains gated behind opening a PR —
// nothing here writes to the document.

import { useCallback, useEffect, useRef, useState } from 'react'
import {
  createDiscussionThread,
  getDiscussionThreadMessages,
  listDiscussionThreads,
  postDiscussionMessage,
  resolveDiscussionThread,
} from '../api'
import { EVENTS, track } from '../lib/analytics'
import LinkedText from './LinkedText'

export default function RFCDiscussionPanel({ slug, viewer }) {
  const [threads, setThreads] = useState([])
  const [messagesByThread, setMessagesByThread] = useState({})
  const [composer, setComposer] = useState('')
  const [activeThreadId, setActiveThreadId] = useState(null)
  const [error, setError] = useState(null)
  const [sending, setSending] = useState(false)
  const bottomRef = useRef(null)

  // Pull threads + messages on mount / slug change.
  useEffect(() => {
    if (!slug) return
    let cancelled = false
    setError(null)
    setThreads([])
    setMessagesByThread({})
    setActiveThreadId(null)
    listDiscussionThreads(slug)
      .then(async ({ items }) => {
        if (cancelled) return
        setThreads(items || [])
        // Pre-load messages for each thread. The list is small (per-RFC,
        // not per-branch) so a fan-out fetch is fine; §19.2 candidate
        // for paging if a hot RFC accumulates lots of threads.
        const collected = {}
        for (const t of items || []) {
          try {
            const { messages } = await getDiscussionThreadMessages(slug, t.id)
            collected[t.id] = messages
          } catch {
            collected[t.id] = []
          }
        }
        if (!cancelled) {
          setMessagesByThread(collected)
          // Default the active thread to the system's lazy whole-doc
          // default (the first row with anchor_kind='whole-doc' and
          // no label) so the composer wires to a real id immediately.
          const dflt = (items || []).find(
            t => t.anchor_kind === 'whole-doc' && !t.label,
          )
          setActiveThreadId(dflt?.id || items?.[0]?.id || null)
        }
      })
      .catch(err => { if (!cancelled) setError(err.message) })
    return () => { cancelled = true }
  }, [slug])

  // Scroll to bottom when messages land in the active thread.
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [activeThreadId, messagesByThread[activeThreadId]?.length])

  const handleSend = useCallback(async () => {
    if (!viewer) { window.location.href = '/auth/login'; return }
    const text = composer.trim()
    if (!text || sending) return
    setSending(true)
    setError(null)
    try {
      // If no thread yet, mint one with the message as its first turn.
      if (!activeThreadId) {
        const { thread_id, message_id } = await createDiscussionThread(slug, { message: text })
        // Re-pull authoritative state — the default whole-doc thread
        // existed pre-this call (the GET creates it lazily), so we
        // either get the existing default's id back from the new
        // thread's row or the prior default; either way the list call
        // is the source of truth.
        const { items } = await listDiscussionThreads(slug)
        setThreads(items || [])
        const { messages } = await getDiscussionThreadMessages(slug, thread_id)
        setMessagesByThread(prev => ({ ...prev, [thread_id]: messages }))
        setActiveThreadId(thread_id)
        void message_id
      } else {
        const { message_id } = await postDiscussionMessage(slug, activeThreadId, { text })
        const { messages } = await getDiscussionThreadMessages(slug, activeThreadId)
        setMessagesByThread(prev => ({ ...prev, [activeThreadId]: messages }))
        void message_id
      }
      setComposer('')
      // v0.15.0 — analytics: fire on a successful discussion post.
      // surface=discussion distinguishes this from PR review comments
      // which fire from PRView with surface=pr. No body text.
      track(EVENTS.COMMENT_POSTED, { rfc_slug: slug, surface: 'discussion' })
    } catch (err) {
      setError(err.message)
    } finally {
      setSending(false)
    }
  }, [composer, sending, viewer, slug, activeThreadId])

  const handleNewThread = useCallback(async () => {
    if (!viewer) { window.location.href = '/auth/login'; return }
    setError(null)
    try {
      const { thread_id } = await createDiscussionThread(slug, { label: null, message: null })
      const { items } = await listDiscussionThreads(slug)
      setThreads(items || [])
      setActiveThreadId(thread_id)
      setMessagesByThread(prev => ({ ...prev, [thread_id]: [] }))
    } catch (err) {
      setError(err.message)
    }
  }, [viewer, slug])

  const handleResolve = useCallback(async (threadId) => {
    if (!viewer) return
    setError(null)
    try {
      await resolveDiscussionThread(slug, threadId)
      const { items } = await listDiscussionThreads(slug)
      setThreads(items || [])
    } catch (err) {
      setError(err.message)
    }
  }, [viewer, slug])

  const onKeyDown = useCallback((e) => {
    if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
      e.preventDefault()
      handleSend()
    }
  }, [handleSend])

  const activeThread = threads.find(t => t.id === activeThreadId) || null
  const activeMessages = messagesByThread[activeThreadId] || []
  const openThreads = threads.filter(t => t.state === 'open')

  return (
    <div className="discussion-panel">
      <div className="discussion-header">
        <span className="discussion-header-title">
          Discussion <span className="beta-chip">Beta</span>
        </span>
        <span className="discussion-header-meta">
          {openThreads.length} open thread{openThreads.length === 1 ? '' : 's'}
          {' · '}contribution requires a PR
        </span>
      </div>

      {threads.length > 1 && (
        <div className="discussion-thread-tabs">
          {threads.map(t => (
            <button
              key={t.id}
              type="button"
              className={`discussion-thread-tab ${t.id === activeThreadId ? 'active' : ''} ${t.state === 'resolved' ? 'resolved' : ''}`}
              onClick={() => setActiveThreadId(t.id)}
              title={t.label || (t.id === activeThreadId ? 'Current thread' : 'Open thread')}
            >
              {t.label || (t.anchor_kind === 'whole-doc' && !t.label ? 'General' : `Thread ${t.id}`)}
              {t.state === 'resolved' && ' ✓'}
            </button>
          ))}
        </div>
      )}

      <div className="discussion-messages">
        {error && <div className="discussion-error">{error}</div>}
        {activeMessages.length === 0 && !error && (
          <div className="discussion-empty">
            <p>
              {viewer
                ? 'No discussion yet. Be the first to comment — discussion lives here without opening a PR. To propose an edit, use Start Contributing above.'
                : 'No discussion yet. Sign in to comment. Discussion lives here without opening a PR; proposed edits still flow through PRs.'}
            </p>
          </div>
        )}
        {activeMessages.map(msg => (
          <DiscussionMessage key={msg.id} message={msg} viewer={viewer} />
        ))}
        <div ref={bottomRef} />
      </div>

      <div className="discussion-composer">
        {viewer ? (
          <>
            <textarea
              className="discussion-composer-textarea"
              value={composer}
              onChange={e => setComposer(e.target.value)}
              onKeyDown={onKeyDown}
              placeholder={
                activeThread?.label
                  ? `Reply in "${activeThread.label}" — Cmd/Ctrl+Enter to send`
                  : 'Discuss this RFC — Cmd/Ctrl+Enter to send'
              }
              disabled={sending}
              rows={3}
            />
            <div className="discussion-composer-actions">
              <button
                type="button"
                className="btn-secondary"
                onClick={handleNewThread}
                disabled={sending}
                title="Open a fresh discussion thread on this RFC"
              >
                New thread
              </button>
              {activeThread
                && activeThread.state === 'open'
                && (activeThread.created_by === viewer.user_id
                    || viewer.role === 'owner'
                    || viewer.role === 'admin') && (
                <button
                  type="button"
                  className="btn-link"
                  onClick={() => handleResolve(activeThread.id)}
                  disabled={sending}
                  title="Mark this discussion thread resolved"
                >
                  Resolve
                </button>
              )}
              <button
                type="button"
                className="btn-primary"
                onClick={handleSend}
                disabled={sending || !composer.trim()}
              >
                {sending ? 'Sending…' : 'Send'}
              </button>
            </div>
          </>
        ) : (
          <div className="discussion-readonly">
            Read-only — <a href="/auth/login">sign in</a> to join the discussion.
            Discussion is in private <strong>Beta</strong>.
          </div>
        )}
      </div>
    </div>
  )
}

function DiscussionMessage({ message, viewer }) {
  const isSystem = message.role === 'system'
  if (isSystem) {
    return (
      <div className="discussion-message system">
        <div className="discussion-system-bubble">{message.text}</div>
      </div>
    )
  }
  return (
    <div className={`discussion-message ${message.role}`}>
      <div className="discussion-message-meta">
        <span className="discussion-message-author">
          @{message.author_login || '—'}
        </span>
        <span className="discussion-message-time">
          {formatTimestamp(message.created_at)}
        </span>
      </div>
      {message.quote && (
        <div className="discussion-message-quote">"{message.quote}"</div>
      )}
      <div className="discussion-message-body">
        <LinkedText segments={message.text_segments} text={message.text} viewer={viewer} canCreate={viewer?.permission_state === 'granted'} />
      </div>
    </div>
  )
}

function formatTimestamp(ts) {
  if (!ts) return ''
  try {
    const d = new Date(ts + (ts.endsWith('Z') ? '' : 'Z'))
    return d.toLocaleString()
  } catch {
    return ts
  }
}
