"""Rating helpers for jokester-p agent.

Provides thin wrappers around RatingTrait for inline and batch rating.
For CLI batch rating without agent setup, use core.training.BatchRatingService.
"""

from __future__ import annotations

from appinfra.log import Logger

from llm_gent.core.memory.rating import Result
from llm_gent.core.traits.builtin.rating import RatingTrait


class InlineRater:
    """Rates jokes during generation using RatingTrait."""

    def __init__(self, lg: Logger, rating_trait: RatingTrait) -> None:
        self._lg = lg
        self._rating = rating_trait

    def rate(self, fact_id: int, joke_text: str) -> None:
        """Rate a joke using all configured providers."""
        try:
            results = self._rating.rate_fact_with_all_providers(
                fact_id=fact_id, content=joke_text, fact_type="solution"
            )
            for result in results:
                self._log_rating(fact_id, result)
        except Exception as e:
            self._lg.warning("inline rating failed", extra={"exception": e, "fact_id": fact_id})

    def _log_rating(self, fact_id: int, result: Result) -> None:
        """Log a single rating result."""
        stars_visual = "★" * result.stars + "☆" * (5 - result.stars)
        reasoning = (
            result.reasoning[:100] + "..." if len(result.reasoning) > 100 else result.reasoning
        )
        self._lg.info(
            f"inline rating: {stars_visual}",
            extra={
                "fact_id": fact_id,
                "stars": result.stars,
                "model": result.model,
                "reasoning": reasoning,
            },
        )


class BatchRater:
    """Batches jokes for efficient batch rating during generation.

    Instead of rating each joke immediately (one LLM call per joke),
    this rater queues jokes and flushes when the batch is full.
    Reduces LLM calls by ~5x with default batch_size=5.

    Usage:
        rater = BatchRater(lg, rating_trait, batch_size=5)
        rater.queue(fact_id, joke_text)  # Auto-flushes when batch is full
        rater.flush()  # Flush remaining before exit
    """

    def __init__(
        self,
        lg: Logger,
        rating_trait: RatingTrait,
        batch_size: int,
        fact_type: str = "solution",
    ) -> None:
        self._lg = lg
        self._rating = rating_trait
        self._batch_size = batch_size
        self._fact_type = fact_type
        self._queue: list[tuple[int, str]] = []

    def queue(self, fact_id: int, content: str) -> None:
        """Queue a fact for rating. Flushes when batch is full."""
        self._queue.append((fact_id, content))
        self._lg.debug(
            "queued for batch rating",
            extra={"fact_id": fact_id, "queue_size": len(self._queue)},
        )
        if len(self._queue) >= self._batch_size:
            self.flush()

    def flush(self) -> int:
        """Rate all queued items. Returns count rated."""
        if not self._queue:
            return 0

        items = self._queue[:]

        try:
            rated = self._rating.rate_items(items, self._fact_type)
            self._queue.clear()
            self._log_batch_results(items, rated)
            return rated
        except Exception as e:
            self._lg.warning("batch rating failed", extra={"exception": e})
            return 0

    def _log_batch_results(self, items: list[tuple[int, str]], rated: int) -> None:
        """Log batch completion and individual ratings."""
        self._lg.debug(
            "flushed batch for rating",
            extra={"count": len(items), "rated": rated},
        )

        for fact_id, content in items:
            stars = self._rating.get_fact_rating(fact_id)
            if stars is not None:
                clamped = max(0, min(5, stars))
                stars_visual = "★" * clamped + "☆" * (5 - clamped)
                preview = content[:80] + "..." if len(content) > 80 else content
                self._lg.info(
                    f"rated: {stars_visual}",
                    extra={"fact_id": fact_id, "stars": stars, "preview": preview},
                )

    @property
    def pending_count(self) -> int:
        """Number of items currently queued."""
        return len(self._queue)
