#!/usr/bin/env python3
"""
Diagnostic #4 — Test SimliClient.Initialize directly to isolate the error
"""
import asyncio
import json
from playwright.async_api import async_playwright

async def run():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=True,
            args=[
                "--use-fake-ui-for-media-stream",
                "--use-fake-device-for-media-stream",
                "--autoplay-policy=no-user-gesture-required",
                "--no-sandbox",
            ]
        )
        context = await browser.new_context(permissions=["microphone"])
        await context.grant_permissions(["microphone"], origin="https://talkingwidget.ai")

        console_messages = []
        page = await context.new_page()
        page.on("console", lambda msg: console_messages.append(f"[{msg.type.upper()}] {msg.text}"))

        print("[D4] Navigating...")
        await page.goto("https://talkingwidget.ai", wait_until="networkidle", timeout=45000)
        await asyncio.sleep(3)

        # Click to get call connected first
        print("[D4] Clicking to get Telnyx call going...")
        await page.click('#avatar-idle-overlay')
        await asyncio.sleep(4)  # wait for WebRTC to connect

        # Now manually test the SimliClient.Initialize call step by step
        print("[D4] Testing SimliClient directly in browser context...")
        simli_test = await page.evaluate("""async () => {
            try {
                // 1. Import SimliClient
                const { SimliClient } = await import('https://esm.sh/simli-client@3');
                if (!SimliClient) return {step: 1, error: 'SimliClient not exported'};

                // 2. Get token from our API
                const resp = await fetch('https://api.sunaivadigital.com/api/simli/session-token', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        faceId: 'd2a5c7c6-fed9-4f55-bcb3-062f7cd20103',
                        model: 'artalk',
                        handleSilence: false,
                        maxSessionLength: 600,
                        maxIdleTime: 90
                    })
                });
                const data = await resp.json();
                if (!data.session_token) return {step: 2, error: 'No session_token', data};

                // 3. Find video/audio elements
                var avatarFrame = document.querySelector('.avatar-frame') || document.getElementById('avatar-vid')?.parentElement;
                if (!avatarFrame) return {step: 3, error: 'no avatar-frame'};

                var simliVideo = document.createElement('video');
                simliVideo.id = 'simli-vid-test';
                simliVideo.autoplay = true;
                simliVideo.playsInline = true;
                simliVideo.muted = true;
                simliVideo.style.display = 'none';
                avatarFrame.appendChild(simliVideo);

                var simliAudio = document.createElement('audio');
                simliAudio.autoplay = true;
                simliAudio.style.display = 'none';
                avatarFrame.appendChild(simliAudio);

                // 4. Initialize SimliClient
                const client = new SimliClient();
                console.log('[D4] SimliClient created:', typeof client);
                console.log('[D4] SimliClient methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(client)).filter(m => typeof client[m] === 'function').join(', '));

                try {
                    await client.Initialize({
                        apiKey: data.session_token,
                        faceID: 'd2a5c7c6-fed9-4f55-bcb3-062f7cd20103',
                        handleSilence: false,
                        videoRef: simliVideo,
                        audioRef: simliAudio,
                        startVideoFirst: false,
                        maxSessionLength: 600,
                        maxIdleTime: 90,
                    });
                    return {step: 4, success: true, message: 'Initialize completed'};
                } catch (initErr) {
                    return {step: 4, error: 'Initialize threw', message: initErr.message, name: initErr.name, stack: initErr.stack};
                }

            } catch (e) {
                return {step: 0, error: 'outer catch', message: e.message, name: e.name, stack: e.stack};
            }
        }""")
        print(f"[D4] SimliClient direct test: {json.dumps(simli_test, indent=2)}")

        # Also check if the issue is the shadow DOM audio element not being detected by MutationObserver
        print("[D4] Testing MutationObserver shadow DOM visibility issue...")
        observer_test = await page.evaluate("""() => {
            var el = document.getElementById('ai-voice-engine');
            if (!el || !el.shadowRoot) return {error: 'no shadow'};
            var sr = el.shadowRoot;
            var audios = sr.querySelectorAll('audio');

            // Can we createMediaElementSource from the shadow audio?
            var results = [];
            try {
                var audioCtx = new AudioContext();
                audios.forEach(function(a, i) {
                    try {
                        // This is the critical test — can we tap the shadow audio element?
                        var src = audioCtx.createMediaElementSource(a);
                        src.connect(audioCtx.destination);
                        results.push({index: i, success: true, srcObject: !!a.srcObject, readyState: a.readyState});
                    } catch (e) {
                        results.push({index: i, error: e.message, name: e.name, srcObject: !!a.srcObject, readyState: a.readyState});
                    }
                });
                audioCtx.close();
            } catch (ctxErr) {
                return {error: 'AudioContext failed', message: ctxErr.message};
            }

            return {
                shadowAudioCount: audios.length,
                results: results,
                // The key insight: MutationObserver can't see shadow DOM nodes
                // unless specifically observing the shadowRoot
                documentAudioCount: document.querySelectorAll('audio').length
            };
        }""")
        print(f"[D4] Shadow audio tapping test: {json.dumps(observer_test, indent=2)}")

        # Check the exact error in the simli-bridge.js startSession:
        # The error is "undefined" -- meaning err.message is undefined
        # This suggests the caught `err` is NOT an Error object
        # Let's check what SimliClient.start() returns/throws
        print("[D4] Testing simliClient.start() error type...")
        start_test = await page.evaluate("""async () => {
            try {
                const { SimliClient } = await import('https://esm.sh/simli-client@3');
                const resp = await fetch('https://api.sunaivadigital.com/api/simli/session-token', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({faceId: 'd2a5c7c6-fed9-4f55-bcb3-062f7cd20103', model: 'artalk', handleSilence: false, maxSessionLength: 600, maxIdleTime: 90})
                });
                const data = await resp.json();

                var frame = document.querySelector('.avatar-frame') || document.getElementById('avatar-vid')?.parentElement;
                var vid = document.createElement('video');
                vid.autoplay = true; vid.playsInline = true; vid.muted = true;
                var aud = document.createElement('audio');
                if (frame) { frame.appendChild(vid); frame.appendChild(aud); }

                const client = new SimliClient();
                await client.Initialize({
                    apiKey: data.session_token,
                    faceID: 'd2a5c7c6-fed9-4f55-bcb3-062f7cd20103',
                    handleSilence: false,
                    videoRef: vid,
                    audioRef: aud,
                    startVideoFirst: false,
                    maxSessionLength: 600,
                    maxIdleTime: 90,
                });

                // Now try start()
                try {
                    const startResult = await client.start();
                    return {start: 'success', result: typeof startResult};
                } catch (startErr) {
                    return {
                        start: 'threw',
                        errType: Object.prototype.toString.call(startErr),
                        message: startErr?.message,
                        isError: startErr instanceof Error,
                        isUndefined: startErr === undefined,
                        isNull: startErr === null,
                        stringified: String(startErr)
                    };
                }
            } catch (e) {
                return {outer: 'threw', message: e.message, stack: e.stack?.slice(0, 300)};
            }
        }""")
        print(f"[D4] SimliClient.start() test: {json.dumps(start_test, indent=2)}")

        print("\n[D4] Console messages:")
        for msg in console_messages:
            print(f"  {msg}")

        await browser.close()

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