"""
Deepslate + Pipecat — Conversation Query Feature Test
=====================================================
Demonstrates DeepslateConversationQueryFrame / DeepslateConversationQueryResultFrame.

The script sends two text messages to build up a short conversation, then fires
a side-channel ConversationQuery asking the model to summarise what was discussed.
The result arrives as a DeepslateConversationQueryResultFrame and is printed to
the console, after which the pipeline shuts down.

The query runs independently of the main voice conversation and does not affect
the conversation history — it is purely a read-only inference over the current
context.

No microphone or speakers are required — all interaction is text-only.

Setup
-----
1. Copy .env.example to .env and fill in your credentials.
2. Run:  python conversation_query_local.py

Requirements
------------
    pip install deepslate-pipecat "pipecat-ai[local]" aiohttp python-dotenv loguru
"""

import asyncio
import os
import signal
import sys

# Ensure the local deepslate-pipecat source is used instead of any globally
# installed version that may not yet contain the conversation-query feature.
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))

from dotenv import load_dotenv
from loguru import logger

from pipecat.frames.frames import Frame, TextFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.frame_processor import FrameProcessor, FrameDirection

from deepslate.pipecat import (
    DeepslateOptions,
    DeepslateRealtimeLLMService,
    DeepslateConversationQueryFrame,
    DeepslateConversationQueryResultFrame,
)

load_dotenv(override=True)

logger.remove()
logger.add(sys.stderr, level="DEBUG")


# ---------------------------------------------------------------------------
# Frame processor — captures DeepslateConversationQueryResultFrame and shuts down
# ---------------------------------------------------------------------------

class ConversationQueryCapture(FrameProcessor):
    """Intercepts DeepslateConversationQueryResultFrame, logs it, then cancels the pipeline."""

    def __init__(self):
        super().__init__()
        self._task: PipelineTask | None = None

    def set_task(self, task: PipelineTask) -> None:
        self._task = task

    async def process_frame(self, frame: Frame, direction: FrameDirection):
        await super().process_frame(frame, direction)

        if isinstance(frame, DeepslateConversationQueryResultFrame):
            _print_query_result(frame.text)
            if self._task:
                await self._task.cancel()
        else:
            await self.push_frame(frame, direction)


# ---------------------------------------------------------------------------
# Helper — pretty-print the query result
# ---------------------------------------------------------------------------

def _print_query_result(text: str) -> None:
    separator = "=" * 60
    logger.info(separator)
    logger.info("Conversation Query Result")
    logger.info(separator)
    logger.info(text)
    logger.info(separator)


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

async def main():
    # 1. Deepslate LLM service — text only, no TTS required
    try:
        opts = DeepslateOptions.from_env(
            system_prompt=(
                "You are a concise AI assistant. "
                "Reply in one or two sentences at most."
            )
        )
    except ValueError as e:
        logger.error(e)
        return

    llm = DeepslateRealtimeLLMService(options=opts)

    # 2. Pipeline: LLM → capture processor
    capture = ConversationQueryCapture()
    pipeline = Pipeline([llm, capture])

    task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=False))
    capture.set_task(task)

    # Allow Ctrl+C to cleanly stop the pipeline
    loop = asyncio.get_event_loop()
    loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(task.cancel()))

    # 3. Schedule the conversation and side-channel query
    async def run_conversation():
        # First user message — give the connection time to establish
        await asyncio.sleep(1)
        logger.info("Sending first text message...")
        await task.queue_frame(TextFrame("Hello! What's the capital of France?"))

        # Second user message — give the LLM time to respond first
        await asyncio.sleep(8)
        logger.info("Sending second text message...")
        await task.queue_frame(TextFrame("And what is the population of Paris?"))

        # Fire a side-channel query after both turns complete.
        # This does NOT add a new turn to the conversation — it reads the
        # existing context and returns a one-shot summary via the result frame.
        await asyncio.sleep(8)
        logger.info("Sending side-channel conversation query...")
        await task.queue_frame(
            DeepslateConversationQueryFrame(
                instructions="Summarise the conversation so far in one sentence.",
            )
        )

    asyncio.create_task(run_conversation())

    # 4. Run
    runner = PipelineRunner()
    logger.info(
        "Conversation query test started. "
        "Two text messages will be sent, then a side-channel query will be issued. "
        "Press Ctrl+C to abort."
    )
    await runner.run(task)


if __name__ == "__main__":
    asyncio.run(main())