from __future__ import annotations

from typing import Any

from app.livekit.runtime.commands import (
    EndServiceOnce,
    ExportSession,
    ExtractSpokenText,
    InterruptSpeech,
    InjectIdleNudge,
    NotifyBridgeDone,
    NotifyBridgeStop,
    RunPipelineInput,
    SetPendingInterrupt,
    SetPipelineInterruption,
    SetPipelineProcessingInterruption,
    StartSession,
    StartSpeech,
    WaitForSpeechPlayout,
)


class RuntimeCommandExecutor:
    def __init__(
        self,
        *,
        agent: Any,
        bridge_gateway: Any,
        export_service: Any,
        session: Any,
        speech_service: Any,
    ) -> None:
        self._agent = agent
        self._bridge_gateway = bridge_gateway
        self._export_service = export_service
        self._session = session
        self._speech_service = speech_service

    def execute_now(self, command: Any) -> Any:
        if isinstance(command, ExtractSpokenText):
            return self._speech_service.extract_spoken_text(command.handle)

        raise TypeError(f"Unsupported synchronous command: {type(command)!r}")

    async def execute(self, command: Any) -> Any:
        if isinstance(command, NotifyBridgeDone):
            await self._bridge_gateway.notify_stage_done(command.reason)
            return None

        if isinstance(command, NotifyBridgeStop):
            await self._bridge_gateway.notify_stop(
                status=command.status,
                reason=command.reason,
                resource=command.resource,
                failed_resources=command.failed_resources,
                phase=command.phase,
            )
            return None

        if isinstance(command, ExportSession):
            await self._export_service.export_session(command.output, command.session_id)
            return None

        if isinstance(command, EndServiceOnce):
            return await self._agent.end_service_once()

        if isinstance(command, InjectIdleNudge):
            if self._agent.pipeline is None:
                raise RuntimeError("Agent backend not ready")
            await self._agent.pipeline.inject_idle_nudge(command.text)
            return None

        if isinstance(command, SetPendingInterrupt):
            await self._agent.set_pending_interrupt(
                listened_text=command.listened_text,
                skipped=command.skipped,
                speech_id=command.speech_id,
            )
            return None

        if isinstance(command, StartSpeech):
            return await self._speech_service.start(
                command.text,
                allow_interruptions=command.allow_interruptions,
                add_to_chat_ctx=command.add_to_chat_ctx,
                audio=command.audio,
            )

        if isinstance(command, WaitForSpeechPlayout):
            await self._speech_service.wait_for_playout(command.handle)
            return None

        if isinstance(command, InterruptSpeech):
            await self._speech_service.interrupt(command.handle, force=command.force)
            return None

        if isinstance(command, SetPipelineInterruption):
            if self._agent.pipeline is None:
                raise RuntimeError("Agent backend not ready")
            await self._agent.pipeline.set_interruption(
                command.interrupted,
                command.listened_text,
                command.skipped,
                command.speech_id,
            )
            return None

        if isinstance(command, SetPipelineProcessingInterruption):
            if self._agent.pipeline is None:
                raise RuntimeError("Agent backend not ready")
            await self._agent.pipeline.set_processing_interruption(
                command.listened_text,
                command.skipped,
                command.speech_id,
            )
            return None

        if isinstance(command, RunPipelineInput):
            if self._agent.pipeline is None:
                raise RuntimeError("Agent backend not ready")
            return await self._agent.pipeline.run(command.user_input)

        if isinstance(command, StartSession):
            await self._session.start(
                agent=self._agent,
                room=command.room,
                room_options=command.room_options,
            )
            return None

        raise TypeError(f"Unsupported command: {type(command)!r}")
