from __future__ import annotations

from contextlib import contextmanager
from contextvars import ContextVar
from dataclasses import dataclass
from collections import deque
from datetime import datetime
from typing import Iterator, Optional

from analogai.foundation.modules.fact.source.fact_source import FactSource


@dataclass(frozen=True)
class FactSentenceContext:
    text: str
    role: str
    source: FactSource
    user_name: Optional[str] = None
    timestamp: Optional[datetime] = None


FactSentenceTuple = tuple[
    str,
    str,
    Optional[str],
    Optional[datetime],
    FactSource,
]


class FactObserverRegistry:
    def __init__(self, require_sentence: bool = False) -> None:
        self._context: ContextVar[deque[FactSentenceContext]] = ContextVar(
            "fact_sentence_context_queue",
            default=deque(),
        )
        self._require_sentence = bool(require_sentence)

    @property
    def require_sentence(self) -> bool:
        return self._require_sentence

    def set_require_sentence(self, require_sentence: bool) -> None:
        self._require_sentence = bool(require_sentence)

    def append_current_sentence(
        self,
        text: str,
        source: FactSource,
        role: str = "user",
        user_name: Optional[str] = None,
        timestamp: Optional[datetime] = None,
    ) -> None:
        _ensure_source(source)
        queue = self._context.get()
        queue.append((text, role, user_name, timestamp, source))

    def append_current_sentence_tuple(self, payload: FactSentenceTuple) -> None:
        _ensure_source(payload[4])
        queue = self._context.get()
        queue.append(payload)

    def clear_current_sentence(self) -> None:
        queue = self._context.get()
        queue.clear()

    def get_current_sentence(self) -> FactSentenceContext | None:
        queue = self._context.get()
        if not queue:
            return None
        return _to_context(queue[0])

    def pop_current_sentence(self) -> FactSentenceContext | None:
        queue = self._context.get()
        if not queue:
            return None
        return _to_context(queue.popleft())

    @contextmanager
    def use_current_sentence(
        self,
        text: str,
        source: FactSource,
        role: str = "user",
        user_name: Optional[str] = None,
        timestamp: Optional[datetime] = None,
    ) -> Iterator[None]:
        _ensure_source(source)
        queue = self._context.get()
        queue.append((text, role, user_name, timestamp, source))
        try:
            yield
        finally:
            if queue:
                queue.pop()


def _to_context(payload: FactSentenceTuple) -> FactSentenceContext:
    return FactSentenceContext(
        text=payload[0],
        role=payload[1],
        user_name=payload[2],
        timestamp=payload[3],
        source=payload[4],
    )


def _ensure_source(source: FactSource) -> None:
    if source is None or not isinstance(source, FactSource):
        raise ValueError("Fact observer requires FactSource")


__all__ = [
    "FactObserverRegistry",
    "FactSentenceContext",
    "FactSentenceTuple",
]
