"""
Deepslate + Pipecat — Chat History Export Feature Test (Audio)
==============================================================
Demonstrates DeepslateExportChatHistoryFrame / DeepslateChatHistoryFrame
with a live audio interaction via microphone and speakers.

The script starts a local audio session so you can speak to the Deepslate
model.  After two conversational turns (detected via a timer), it requests
a full chat history export and pretty-prints every message in the returned
history — including audio transcript blocks.

Setup
-----
1. Copy .env.example to .env and fill in your credentials
   (DEEPSLATE_API_KEY, ELEVENLABS_API_KEY, etc.).
2. Run:  python chat_history_export_audio_local.py

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

import asyncio
import json
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 chat-history-export 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
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 pipecat.transports.local.audio import LocalAudioTransport, LocalAudioTransportParams

from deepslate.pipecat import (
    DeepslateOptions,
    DeepslateRealtimeLLMService,
    DeepslateExportChatHistoryFrame,
    DeepslateChatHistoryFrame,
    ElevenLabsTtsConfig,
)

load_dotenv(override=True)

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

# How long to record audio before requesting the history export (seconds).
CONVERSATION_DURATION_SECS = 30


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

class ChatHistoryCapture(FrameProcessor):
    """Intercepts DeepslateChatHistoryFrame, 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, DeepslateChatHistoryFrame):
            _print_chat_history(frame.messages)
            if self._task:
                await self._task.cancel()
        else:
            await self.push_frame(frame, direction)


# ---------------------------------------------------------------------------
# Helper — pretty-print the exported history
# ---------------------------------------------------------------------------

def _print_chat_history(messages: list) -> None:
    separator = "=" * 60
    logger.info(separator)
    logger.info(f"Chat History Export — {len(messages)} message(s)")
    logger.info(separator)

    for i, msg in enumerate(messages, 1):
        role = msg.get("role", "?")
        ephemeral = msg.get("ephemeral", False)
        status = msg.get("delivery_status", "?")
        content = msg.get("content", [])

        tag = " [ephemeral]" if ephemeral else ""
        logger.info(f"[{i}] role={role}{tag}  status={status}")

        for block in content:
            block_type = block.get("type", "?")
            if block_type == "text":
                text = block.get("text", "")[:200]
                logger.info(f"      text: {text}")
            elif block_type == "input_audio":
                transcript = block.get("transcription", "")[:200]
                logger.info(f"      audio transcript: {transcript}")
            elif block_type == "tool_call":
                logger.info(
                    f"      tool_call: {block.get('name')}  params={block.get('parameters', {})}"
                )
            elif block_type == "tool_result":
                result = block.get("result", "")[:120]
                logger.info(f"      tool_result id={block.get('id')}: {result}")
            elif block_type == "thoughts":
                logger.info(f"      thoughts: {block.get('text', '')[:120]}")
            elif block_type == "instructions":
                logger.info(f"      instructions: {block.get('text', '')[:120]}")
            else:
                logger.info(f"      {block_type}: {block}")

    logger.info(separator)
    logger.info("Raw export:")
    logger.info(json.dumps(_strip_bytes(messages), indent=2))


def _strip_bytes(obj):
    if isinstance(obj, bytes):
        return None
    if isinstance(obj, dict):
        return {k: _strip_bytes(v) for k, v in obj.items() if not isinstance(v, bytes)}
    if isinstance(obj, list):
        return [_strip_bytes(item) for item in obj]
    return obj


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

async def main():
    # 1. Local audio transport — microphone in, speakers out
    transport = LocalAudioTransport(
        params=LocalAudioTransportParams(
            audio_in_enabled=True,
            audio_out_enabled=True,
            audio_in_sample_rate=16000,
            audio_out_sample_rate=16000,
            audio_in_channels=1,
            audio_out_channels=1,
        )
    )

    # 2. Deepslate LLM service with TTS
    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

    tts = ElevenLabsTtsConfig.from_env()
    llm = DeepslateRealtimeLLMService(options=opts, tts_config=tts)

    # 3. Pipeline: mic → LLM → capture → speakers
    capture = ChatHistoryCapture()
    pipeline = Pipeline([
        transport.input(),
        llm,
        capture,
        transport.output(),
    ])

    task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
    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()))

    # 4. After CONVERSATION_DURATION_SECS, request the chat history export
    async def schedule_export():
        await asyncio.sleep(CONVERSATION_DURATION_SECS)
        logger.info("Requesting chat history export (await_pending=False)...")
        await task.queue_frame(DeepslateExportChatHistoryFrame(await_pending=False))

    asyncio.create_task(schedule_export())

    # 5. Run
    runner = PipelineRunner()
    logger.info(
        f"Audio chat history export test started. "
        f"Speak into your microphone for up to {CONVERSATION_DURATION_SECS}s, "
        f"then the full history will be exported automatically. "
        f"Press Ctrl+C to abort."
    )
    await runner.run(task)


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