"""Demo-only pipeline decorator for honest fuzzy replay disclosure."""

import time
from collections.abc import AsyncGenerator
from contextlib import aclosing

from app.adapters.demo.catalog import DemoCatalog
from app.adapters.demo.recommender import DemoRecommender, parse_recorded_intent
from app.adapters.demo.scenario import select_replay_scenario
from app.config import Settings
from app.domain.models import ConversationTurn, PreviousRecommendation, Track
from app.pipeline.event import PipelineEvent, PipelineMetadataEvent, PipelineWarningEvent
from app.pipeline.grounding import Grounder
from app.pipeline.orchestrator import RecommendationPipeline
from app.ports.protocols import MusicCatalog

DEMO_REPLAY_CODE = "demo_replay"


class DemoReplayPipeline:
    """Decorate the real pipeline with a fuzzy-replay warning event."""

    def __init__(self, settings: Settings) -> None:
        """Create one cache-preserving pipeline with a replay recommender."""
        self.settings = settings
        self.pipeline = RecommendationPipeline(DemoRecommender(settings), settings)

    async def stream(
        self,
        session_id: str,
        request_id: str,
        catalog: MusicCatalog,
        query: str,
        history: tuple[ConversationTurn, ...],
        previous_recommendations: tuple[PreviousRecommendation, ...],
    ) -> AsyncGenerator[PipelineEvent]:
        """Stream the selected fixture and disclose non-exact selection."""
        match = select_replay_scenario(query, bool(previous_recommendations))
        seeded_pool = await self._prepare_refinement_pool(catalog)
        event_stream = self.pipeline.stream(
            session_id,
            request_id,
            catalog,
            query,
            history,
            previous_recommendations,
            seeded_pool=seeded_pool,
        )
        async with aclosing(event_stream) as events:
            async for event in events:
                yield event
                if isinstance(event, PipelineMetadataEvent) and not match.is_exact:
                    yield PipelineWarningEvent(
                        code=DEMO_REPLAY_CODE,
                        message=f'Demo replay is showing the recorded "{match.chip}" scenario.',
                    )

    async def _prepare_refinement_pool(
        self,
        catalog: MusicCatalog,
    ) -> tuple[Track, ...] | None:
        if not isinstance(catalog, DemoCatalog):
            return None
        intent_bodies = catalog.cassette.intent_response_bodies
        if len(intent_bodies) < 2:
            return None
        # Refinement cassettes record the parent intent first and refinement intent last.
        parent_intent = parse_recorded_intent(intent_bodies[0])
        result = await Grounder(self.settings).ground(
            catalog,
            parent_intent.candidates,
            # The recorded parent pool is listener-neutral, so replay has no known-track exclusions.
            frozenset(),
            parent_intent.familiarity,
            self.settings.rerank_count + self.settings.rerank_pool_buffer,
            time.monotonic() + self.settings.request_deadline_seconds,
        )
        return result.tracks
