"""Context compression worker."""

import structlog

from navi.config import settings
from navi.core.compressor import ContextCompressor, should_compress

from .base import Worker, WorkerContext, WorkerResult

log = structlog.get_logger()


class CompressionWorker(Worker):
    """
    Compresses session.context when it approaches the token limit.
    session.messages (full display history) is never modified.

    The token gate uses the real prompt-token count from the last LLM call
    (``ctx.context_tokens``); the compression itself is delegated to
    ``ContextCompressor.compress_and_save_session`` so the post-turn path gets
    the same pipeline as the pre/mid-turn paths: retry on LLM failure,
    hard-truncate fallback, the result-None token-budget fallback, the
    65%-target safety net, real-baseline clearing, and message-window
    archiving. The worker used to call ``compress_context`` directly with its
    own save logic — a second, weaker pipeline that silently no-op'ed whenever
    the summarizer LLM failed while the context stayed over the threshold.
    """

    async def run(self, session, ctx: WorkerContext) -> WorkerResult:
        if not settings.context_compression_enabled:
            return WorkerResult()
        if ctx.context_tokens is None:
            return WorkerResult()
        if not should_compress(ctx.context_tokens, ctx.max_context_tokens,
                               settings.context_compression_threshold):
            return WorkerResult()

        compressor = ContextCompressor()
        compressor.set_profile(ctx.profile)
        try:
            # Mirror the midturn path: a long autonomous turn (one user
            # message + many tool iterations) is a single conversational
            # turn, so turn-based compression alone finds nothing to
            # summarize. The intra-turn fallback lets the worker compress
            # it too — without this the post-turn worker always no-ops for
            # the navi_code shape.
            event = await compressor.compress_and_save_session(
                session=session,
                session_store=ctx.session_store,
                llm=ctx.llm,
                model=ctx.model,
                temperature=settings.context_summary_temperature,
                session_id=ctx.session_id,
                reason="postturn",
                keep_recent=settings.context_keep_recent,
                max_tokens=settings.context_summary_max_tokens,
                keep_recent_messages=max(12, settings.context_keep_recent * 2),
            )
        except Exception:
            # compress_and_save_session already retries and hard-truncates
            # internally; reaching here means something unexpected broke
            # (store failure etc.) — log and leave the session untouched.
            log.warning("compression_worker.failed", session_id=ctx.session_id, exc_info=True)
            return WorkerResult()

        if event is None:
            return WorkerResult()

        log.info(
            "compression_worker.done",
            session_id=ctx.session_id,
            before=event.messages_before,
            after=event.messages_after,
        )
        return WorkerResult(events=[event])