"""Sentence buffer for real-time transcription."""

from __future__ import annotations

import threading
import time
from collections import deque
from dataclasses import dataclass


@dataclass
class SentenceEntry:
    timestamp: float
    text: str


class SentenceBuffer:
    """Thread-safe buffer of the last N finalized transcript sentences.

    Unlike a time-based buffer, this evicts by count — giving a predictable
    token budget for every Claude query.

    The last interim (in-progress) sentence is tracked separately so the
    overlay can show it in real-time without polluting the committed history.
    """

    def __init__(self, max_sentences: int = 50) -> None:
        self._max_sentences = max_sentences
        self._sentences: deque[SentenceEntry] = deque(maxlen=max_sentences)
        self._last_interim: str | None = None
        self._lock = threading.Lock()
        self._on_sentence: list = []  # callbacks: (sentence: str) -> None

    def add_sentence_listener(self, callback) -> None:
        """Register a callback fired on every new final sentence."""
        self._on_sentence.append(callback)

    def remove_sentence_listener(self, callback) -> None:
        self._on_sentence = [c for c in self._on_sentence if c is not callback]

    def append_final(self, text: str) -> None:
        """Commit a finalized sentence from Deepgram/Whisper."""
        text = text.strip()
        if not text:
            return
        with self._lock:
            self._sentences.append(SentenceEntry(timestamp=time.time(), text=text))
            self._last_interim = None
        # Fire callbacks outside the lock
        for cb in list(self._on_sentence):
            try:
                cb(text)
            except Exception:
                pass

    def set_interim(self, text: str) -> None:
        """Update the current in-progress sentence (overlay display only)."""
        with self._lock:
            self._last_interim = text.strip() if text.strip() else None

    def get_last_n(self, n: int | None = None) -> list[str]:
        """Return the last N finalized sentences as a list."""
        with self._lock:
            sentences = list(self._sentences)
        if n is not None:
            sentences = sentences[-n:]
        return [e.text for e in sentences]

    def get_last_n_as_text(self, n: int | None = None) -> str:
        """Return the last N finalized sentences joined by newlines."""
        return "\n".join(self.get_last_n(n))

    def get_interim(self) -> str | None:
        """Return the current in-progress sentence, or None."""
        with self._lock:
            return self._last_interim

    def get_display_text(self, max_chars: int = 200) -> str:
        """Return last 2 sentences + interim for overlay display."""
        with self._lock:
            recent = list(self._sentences)[-2:]
            texts = [e.text for e in recent]
            if self._last_interim:
                texts.append(f"[{self._last_interim}]")
        combined = " ".join(texts)
        if len(combined) > max_chars:
            combined = "..." + combined[-(max_chars - 3):]
        return combined

    def get_full_transcript(self) -> str:
        """Return all sentences — for saving to .live.txt file."""
        with self._lock:
            return "\n".join(e.text for e in self._sentences)

    def clear(self) -> None:
        with self._lock:
            self._sentences.clear()
            self._last_interim = None

    def __len__(self) -> int:
        with self._lock:
            return len(self._sentences)
