"""Live pipecat smoke for the proxy realtime websocket.

A realism layer on top of test_realtime_e2e: instead of speaking the GA protocol
by hand, drive the proxy through the shared LiteLLMRealtimeLLMService (pipecat's
GA service with proxy-specific overrides, keepalive pings disabled) with its
base_url pointed at the proxy and the model swapped per provider. It confirms the
audio and function-call wiring survives the round-trip. Assertions are coarse;
the raw-websocket suite is the source of truth.

The harness is synchronous, so each test stays a normal sync function and drives
the async pipecat pipeline with asyncio.run. Skips unless pipecat is installed:

    uv pip install "pipecat-ai[openai]"

Known caveat: pipecat tool calling over the realtime service has been flaky
upstream (pipecat-ai/pipecat#2544). A failure here with the matching raw-ws tool
test passing points at pipecat, not litellm.
"""

# pipecat is an optional, dynamically typed dependency loaded behind importorskip,
# so its symbols are Unknown to the type checker; relax those rules for this file.
# pyright: reportUnknownMemberType=false, reportUnknownVariableType=false, reportUnknownArgumentType=false, reportAttributeAccessIssue=false, reportUntypedBaseClass=false, reportUnknownParameterType=false, reportMissingParameterType=false

import asyncio

import pytest

from e2e_config import ws_base_url
from realtime_client import (
    PROVIDERS,
    RealtimeProvider,
    realtime_model,
)

pytestmark = pytest.mark.e2e

pytest.importorskip("pipecat", reason="pipecat-ai not installed")

from pipecat.adapters.schemas.function_schema import FunctionSchema  # noqa: E402
from pipecat.adapters.schemas.tools_schema import ToolsSchema  # noqa: E402
from pipecat.frames.frames import (  # noqa: E402
    EndFrame,
    Frame,
    LLMRunFrame,
    TranscriptionFrame,
    TTSTextFrame,
)
from pipecat.pipeline.pipeline import Pipeline  # noqa: E402
from pipecat.pipeline.runner import PipelineRunner  # noqa: E402
from pipecat.pipeline.task import PipelineTask  # noqa: E402
from pipecat.processors.aggregators.llm_context import LLMContext  # noqa: E402
from pipecat.processors.aggregators.llm_response_universal import (  # noqa: E402
    LLMContextAggregatorPair,
)
from pipecat.processors.frame_processor import (  # noqa: E402
    FrameDirection,
    FrameProcessor,
)
from pipecat.services.llm_service import FunctionCallParams  # noqa: E402

from pipecat_service import LiteLLMRealtimeLLMService  # noqa: E402

# Vertex native-audio live is flaky through pipecat tool calling (upstream
# pipecat-ai/pipecat#2544); raw-ws tool_call_round_trip[vertex_ai] is the
# source of truth for that provider. Keep openai/azure/gemini here.
PROVIDER_PARAMS = [
    pytest.param(p, id=p.id) for p in PROVIDERS if p.id != "vertex_ai"
]

WEATHER_TOOL = ToolsSchema(
    standard_tools=[
        FunctionSchema(
            name="get_weather",
            description="Get the current temperature in Fahrenheit for a city.",
            properties={"city": {"type": "string"}},
            required=["city"],
        )
    ]
)


class _CaptureText(FrameProcessor):
    def __init__(self) -> None:
        super().__init__()
        self.texts: list[str] = []

    async def process_frame(self, frame: Frame, direction: FrameDirection) -> None:
        await super().process_frame(frame, direction)
        if isinstance(frame, (TTSTextFrame, TranscriptionFrame)):
            self.texts.append(frame.text)
        await self.push_frame(frame, direction)


async def _run_pipeline(key: str, model: str) -> tuple[bool, bool]:
    tool_called = asyncio.Event()

    async def get_weather(params: FunctionCallParams) -> None:
        tool_called.set()
        await params.result_callback({"city": "Paris", "temperature_f": 72})

    llm = LiteLLMRealtimeLLMService(
        api_key=key, base_url=f"{ws_base_url()}/v1/realtime", model=model
    )
    llm.register_function("get_weather", get_weather)

    context = LLMContext(tools=WEATHER_TOOL)
    aggregator = LLMContextAggregatorPair(context)
    capture = _CaptureText()
    task = PipelineTask(
        Pipeline([aggregator.user(), llm, capture, aggregator.assistant()])
    )

    await task.queue_frames(
        [
            TranscriptionFrame(
                "What's the weather in Paris?", user_id="e2e", timestamp=""
            ),
            LLMRunFrame(),
        ]
    )
    try:
        await asyncio.wait_for(PipelineRunner().run(task), timeout=45)
    except asyncio.TimeoutError:
        await task.queue_frame(EndFrame())
    return tool_called.is_set(), bool(capture.texts)


@pytest.mark.parametrize("provider", PROVIDER_PARAMS)
def test_pipecat_tool_smoke(
    scoped_key: str,
    realtime_models: dict[str, str],
    provider: RealtimeProvider,
) -> None:
    model = realtime_model(provider, realtime_models)

    tool_called, produced_text = asyncio.run(_run_pipeline(scoped_key, model))

    assert tool_called, "pipecat did not invoke the get_weather callback"
    assert produced_text, "pipecat produced no assistant text frames"
