#!/usr/bin/env python3
"""
Standalone script to test Nova Sonic provider with full receive functionality.

This works because we control the entire event loop and can run receive
as a background task in the same loop.

Usage:
    python scripts/test_nova_sonic_provider.py
"""

import asyncio
import signal
import sys
from typing import List

# Add src to path
sys.path.insert(0, "src")


async def main():
    from tau2.voice.audio_native.nova.events import BaseNovaEvent, parse_nova_event
    from tau2.voice.audio_native.nova.provider import NovaSonicProvider, NovaVADConfig

    print("=" * 60)
    print("Nova Sonic Provider Test Script")
    print("=" * 60)

    provider = NovaSonicProvider()
    received_events: List[BaseNovaEvent] = []
    is_active = True
    got_completion_end = False

    async def receive_loop():
        """Background task to receive events."""
        nonlocal got_completion_end

        print("\n[RECEIVE] Starting receive loop...")

        # Wait for output stream to be ready
        ready = await provider._ensure_output_stream()
        if not ready:
            print("[RECEIVE] Failed to get output stream!")
            return

        print("[RECEIVE] Output stream ready, listening for events...")

        while is_active and not got_completion_end:
            try:
                event_data = await provider._read_next_event()
                if event_data is None:
                    print("[RECEIVE] Stream ended")
                    break

                event = parse_nova_event(event_data)
                received_events.append(event)

                # Log the event
                if event.event_type == "audioOutput":
                    content_len = len(getattr(event, "content", "") or "")
                    print(f"[RECEIVE] audioOutput ({content_len} b64 chars)")
                elif event.event_type == "completionEnd":
                    print(f"[RECEIVE] completionEnd - response complete!")
                    got_completion_end = True
                else:
                    print(f"[RECEIVE] {event.event_type}")

            except Exception as e:
                print(f"[RECEIVE] Error: {e}")
                break

        print(f"[RECEIVE] Loop ended. Total events: {len(received_events)}")

    try:
        # 1. Connect
        print("\n1. Connecting to Nova Sonic...")
        await provider.connect()
        print("   ✅ Connected!")

        # 2. Configure session
        print("\n2. Configuring session...")
        await provider.configure_session(
            system_prompt="You are a helpful assistant. Keep responses very brief (1-2 sentences max).",
            tools=[],
            vad_config=NovaVADConfig(),
        )
        print("   ✅ Session configured!")

        # 3. Start audio content stream (required by Nova Sonic)
        print("\n3. Starting audio content stream...")
        audio_content_id = await provider.start_audio_stream()
        print("   ✅ Audio stream started!")

        # 4. Start receive loop as background task
        print("\n4. Starting background receive task...")
        receive_task = asyncio.create_task(receive_loop())

        # Give it a moment to start
        await asyncio.sleep(0.1)

        # 5. Send real audio with speech (generated by ElevenLabs)
        print("\n5. Loading and sending test audio with speech...")

        import os
        import wave

        # Use our generated test audio files
        audio_path = "tests/test_voice/test_audio_native/testdata/hello.wav"
        if os.path.exists(audio_path):
            with wave.open(audio_path, "rb") as wav:
                sample_rate = wav.getframerate()
                n_channels = wav.getnchannels()
                sample_width = wav.getsampwidth()
                n_frames = wav.getnframes()

                print(
                    f"   Audio file: {sample_rate}Hz, {n_channels}ch, {sample_width * 8}bit, {n_frames} frames"
                )

                # Read raw PCM audio
                raw_audio = wav.readframes(n_frames)
                duration_ms = len(raw_audio) / 32  # 32 bytes/ms at 16kHz 16-bit

                print(
                    f"   Sending {len(raw_audio)} bytes ({duration_ms:.0f}ms of speech)"
                )
                await provider.send_audio(raw_audio, audio_content_id)
                print("   ✅ Speech audio sent!")

                # VAD needs silence AFTER speech to detect turn end
                # Send 1 second of silence to trigger VAD
                silence_1s = b"\x00" * 32000  # 1 second at 16kHz 16-bit
                print("   Sending 1s of silence for VAD...")
                await provider.send_audio(silence_1s, audio_content_id)
                print("   ✅ Silence sent!")

                # Don't end the audio content yet - let VAD detect the turn end
                # The model should start responding after detecting silence
                print("   Waiting for VAD to detect turn end...")
        else:
            print(f"   ⚠️ Audio file not found: {audio_path}")
            print(
                "   Run: python src/tau2/voice/audio_native/nova/generate_test_audio.py"
            )
            return

        # 6. Wait for response (with timeout)
        print("\n6. Waiting for response (max 30 seconds)...")
        try:
            # Wait until we get completionEnd or timeout
            for _ in range(300):  # 30 seconds max
                if got_completion_end:
                    print("   ✅ Got completion!")
                    break
                await asyncio.sleep(0.1)
            else:
                print("   ⚠️ Timeout waiting for completion")
        except Exception as e:
            print(f"   ❌ Error: {e}")

        # 6. Stop receive loop
        is_active = False

        # Cancel the task if still running
        if not receive_task.done():
            receive_task.cancel()
            try:
                await receive_task
            except asyncio.CancelledError:
                pass

        # 7. Show results
        print("\n" + "=" * 60)
        print("RESULTS")
        print("=" * 60)
        print(f"Total events received: {len(received_events)}")
        print("\nEvent types:")
        event_types = {}
        for e in received_events:
            event_types[e.event_type] = event_types.get(e.event_type, 0) + 1
        for t, count in sorted(event_types.items()):
            print(f"  - {t}: {count}")

        # Count audio data
        audio_events = [e for e in received_events if e.event_type == "audioOutput"]
        total_audio_chars = sum(
            len(getattr(e, "content", "") or "") for e in audio_events
        )
        print(
            f"\nAudio data: {len(audio_events)} chunks, {total_audio_chars} b64 chars total"
        )

        if total_audio_chars > 0:
            # Rough estimate: b64 is ~4/3 of raw bytes, PCM16 16kHz = 32000 bytes/sec
            raw_bytes = total_audio_chars * 3 // 4
            duration_ms = raw_bytes * 1000 // 32000
            print(f"Estimated audio duration: ~{duration_ms}ms")

        # Check for text transcript
        text_events = [e for e in received_events if e.event_type == "textOutput"]
        if text_events:
            print("\nTranscripts:")
            for e in text_events:
                content = getattr(e, "content", "")
                role = getattr(e, "role", "UNKNOWN")
                if content:
                    print(f"  [{role}] '{content}'")

        if len(received_events) > 0:
            print("\n✅ SUCCESS - Nova Sonic provider works!")
        else:
            print("\n❌ FAILED - No events received")

    except Exception as e:
        print(f"\n❌ Error: {e}")
        import traceback

        traceback.print_exc()

    finally:
        # 9. Disconnect
        print("\n9. Disconnecting...")
        is_active = False
        await provider.disconnect()
        print("   ✅ Disconnected!")


if __name__ == "__main__":
    # Set up timeout
    def timeout_handler(signum, frame):
        print("\n\n❌ SCRIPT TIMEOUT (60s)")
        sys.exit(1)

    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(60)

    # Run
    asyncio.run(main())
