#!/usr/bin/env python3
"""
Test talkingwidget.ai — voice widget connection + Simli avatar activation.
Runs via Playwright directly (not MCP) to capture full state.
"""

import asyncio
import json
import time
from pathlib import Path
from playwright.async_api import async_playwright

SCREENSHOTS_DIR = Path("/mnt/e/genesis-system/qa_reports")
SCREENSHOTS_DIR.mkdir(exist_ok=True)

JS_INITIAL_STATE = """
() => JSON.stringify({
    avatarStageClasses: document.getElementById('avatar-stage')?.className,
    simliVidDisplay: document.getElementById('simli-vid')?.style?.display,
    simliVidExists: !!document.getElementById('simli-vid'),
    voiceElExists: !!document.getElementById('ai-voice-engine'),
    voiceElAgentId: document.getElementById('ai-voice-engine')?.getAttribute('agent-id')?.slice(0,20),
    sessionTokenPrefetched: !!window._simliSessionToken,
    bridgeState: window._simliBridgeState,
})
"""

JS_POST_CLICK_STATE = """
() => JSON.stringify({
    avatarStageClasses: document.getElementById('avatar-stage')?.className,
    simliVidDisplay: document.getElementById('simli-vid')?.style?.display,
    simliVidExists: !!document.getElementById('simli-vid'),
    inCallClass: document.getElementById('avatar-stage')?.classList?.contains('in-call'),
    vaStartFired: window._vaStartFired,
})
"""

JS_VIDEO_VISIBLE = """
() => {
    const vid = document.getElementById('simli-vid');
    if (!vid) return JSON.stringify({exists: false});
    const rect = vid.getBoundingClientRect();
    return JSON.stringify({
        exists: true,
        display: vid.style.display,
        visibility: vid.style.visibility,
        opacity: vid.style.opacity,
        width: rect.width,
        height: rect.height,
        top: rect.top,
        readyState: vid.readyState,
        networkState: vid.networkState,
        currentSrc: vid.currentSrc,
        videoWidth: vid.videoWidth,
        videoHeight: vid.videoHeight,
        paused: vid.paused,
        srcObject: !!vid.srcObject,
    });
}
"""

JS_ALL_ELEMENTS = """
() => {
    const els = ['avatar-stage', 'simli-vid', 'ai-voice-engine', 'avatar-idle-overlay'];
    const result = {};
    for (const id of els) {
        const el = document.getElementById(id);
        if (!el) { result[id] = null; continue; }
        result[id] = {
            tagName: el.tagName,
            className: el.className,
            display: getComputedStyle(el).display,
            visibility: getComputedStyle(el).visibility,
            opacity: getComputedStyle(el).opacity,
            innerHTML_snippet: el.innerHTML.slice(0, 200),
        };
    }
    return JSON.stringify(result);
}
"""

JS_CONSOLE_SIMLI = """
() => {
    // Check window state for Simli markers
    return JSON.stringify({
        simliLoaded: typeof window.SimliClient !== 'undefined',
        simliClientExists: !!window._simliClient,
        vaStartFired: window._vaStartFired,
        simliSessionToken: window._simliSessionToken ? 'present' : 'missing',
        bridgeState: window._simliBridgeState || 'not-set',
        telnyxConnected: window._telnyxConnected,
        allWindowKeys: Object.keys(window).filter(k => k.toLowerCase().includes('simli') || k.toLowerCase().includes('telnyx') || k.toLowerCase().includes('va') || k.toLowerCase().includes('bridge')),
    });
}
"""

async def run_test():
    console_logs = []
    console_errors = []

    print("\n" + "="*60)
    print("TALKINGWIDGET.AI — VOICE + SIMLI AVATAR TEST")
    print("="*60)

    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=True,
            args=[
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--autoplay-policy=no-user-gesture-required',
                '--use-fake-ui-for-media-stream',
                '--use-fake-device-for-media-stream',
                '--disable-web-security',
            ]
        )

        context = await browser.new_context(
            viewport={'width': 1280, 'height': 900},
            permissions=['microphone', 'camera'],
        )

        page = await context.new_page()

        # Capture console logs
        def on_console(msg):
            text = msg.text
            console_logs.append({'type': msg.type, 'text': text})
            if any(kw in text for kw in ['Simli', 'simli', 'Telnyx', 'telnyx', 'ERROR', 'error', 'voice', 'avatar']):
                print(f"  [CONSOLE/{msg.type.upper()}] {text[:200]}")

        def on_pageerror(err):
            console_errors.append(str(err))
            print(f"  [PAGE ERROR] {str(err)[:200]}")

        page.on('console', on_console)
        page.on('pageerror', on_pageerror)

        # STEP 1: Navigate
        print("\n[STEP 1] Navigating to https://talkingwidget.ai ...")
        try:
            await page.goto('https://talkingwidget.ai', wait_until='domcontentloaded', timeout=30000)
            print(f"  Page title: {await page.title()}")
            print(f"  URL: {page.url}")
        except Exception as e:
            print(f"  ERROR navigating: {e}")

        # STEP 2: Wait 4 seconds
        print("\n[STEP 2] Waiting 4 seconds for SDK init + Simli prefetch ...")
        await asyncio.sleep(4)
        print("  Done waiting.")

        # STEP 3: Screenshot of full page
        print("\n[STEP 3] Taking screenshot of full page ...")
        ss1_path = str(SCREENSHOTS_DIR / "talkingwidget_01_initial.png")
        await page.screenshot(path=ss1_path, full_page=True)
        print(f"  Screenshot saved: {ss1_path}")

        # STEP 4: Inject JS to check initial state
        print("\n[STEP 4] Checking initial JS state ...")
        try:
            initial_state_raw = await page.evaluate(JS_INITIAL_STATE.strip().lstrip('() => '))
            initial_state = json.loads(initial_state_raw)
            print(f"  Initial state: {json.dumps(initial_state, indent=4)}")
        except Exception as e:
            print(f"  ERROR evaluating initial state: {e}")
            initial_state = {}

        # Also check all key elements
        print("\n  Checking all key elements ...")
        try:
            elements_raw = await page.evaluate(JS_ALL_ELEMENTS.strip().lstrip('() => '))
            elements = json.loads(elements_raw)
            for el_id, data in elements.items():
                if data:
                    print(f"    #{el_id}: tag={data['tagName']}, display={data['display']}, class={data['className'][:80]}")
                else:
                    print(f"    #{el_id}: NOT FOUND")
        except Exception as e:
            print(f"  ERROR checking elements: {e}")

        # Check Simli/Telnyx window state
        print("\n  Checking Simli/Telnyx window state ...")
        try:
            simli_state_raw = await page.evaluate(JS_CONSOLE_SIMLI.strip().lstrip('() => '))
            simli_state = json.loads(simli_state_raw)
            print(f"  Simli/window state: {json.dumps(simli_state, indent=4)}")
        except Exception as e:
            print(f"  ERROR checking Simli state: {e}")

        # Check video element
        print("\n  Checking #simli-vid video element ...")
        try:
            vid_raw = await page.evaluate(JS_VIDEO_VISIBLE.strip().lstrip('() => '))
            vid_state = json.loads(vid_raw)
            print(f"  Video element state: {json.dumps(vid_state, indent=4)}")
        except Exception as e:
            print(f"  ERROR checking video: {e}")

        # STEP 5: Click the hero avatar / mic button overlay
        print("\n[STEP 5] Attempting to click #avatar-idle-overlay ...")

        # Check if it exists first
        overlay = await page.query_selector('#avatar-idle-overlay')
        if overlay:
            print("  #avatar-idle-overlay FOUND")
            # Check if it's visible
            is_visible = await overlay.is_visible()
            print(f"  Is visible: {is_visible}")
            if is_visible:
                await overlay.click()
                print("  Clicked #avatar-idle-overlay")
            else:
                print("  Overlay not visible — trying force click ...")
                await overlay.click(force=True)
                print("  Force-clicked #avatar-idle-overlay")
        else:
            print("  #avatar-idle-overlay NOT FOUND — checking alternatives ...")
            # Try finding by other means
            btn_selectors = [
                'button[id*="avatar"]',
                '.mic-button',
                '#mic-btn',
                '[class*="overlay"]',
                '[class*="mic"]',
                '.avatar-overlay',
                '#start-call',
            ]
            for sel in btn_selectors:
                el = await page.query_selector(sel)
                if el:
                    print(f"  Found alternative: {sel}")
                    await el.click()
                    print(f"  Clicked {sel}")
                    break
            else:
                print("  No clickable button found — trying page center click ...")
                await page.mouse.click(640, 450)

        # STEP 6: Wait 5 seconds
        print("\n[STEP 6] Waiting 5 seconds post-click ...")
        await asyncio.sleep(5)
        print("  Done waiting.")

        # STEP 7: Screenshot post-click
        print("\n[STEP 7] Taking post-click screenshot ...")
        ss2_path = str(SCREENSHOTS_DIR / "talkingwidget_02_post_click.png")
        await page.screenshot(path=ss2_path, full_page=True)
        print(f"  Screenshot saved: {ss2_path}")

        # STEP 8: Check post-click JS state
        print("\n[STEP 8] Checking post-click JS state ...")
        try:
            post_state_raw = await page.evaluate(JS_POST_CLICK_STATE.strip().lstrip('() => '))
            post_state = json.loads(post_state_raw)
            print(f"  Post-click state: {json.dumps(post_state, indent=4)}")
        except Exception as e:
            print(f"  ERROR evaluating post-click state: {e}")
            post_state = {}

        # STEP 9: Check video visibility
        print("\n[STEP 9] Checking if #simli-vid is visible ...")
        try:
            vid_raw2 = await page.evaluate(JS_VIDEO_VISIBLE.strip().lstrip('() => '))
            vid_state2 = json.loads(vid_raw2)
            print(f"  Video element state (post-click): {json.dumps(vid_state2, indent=4)}")

            # Is the video actually live?
            if vid_state2.get('srcObject'):
                print("  [RESULT] srcObject IS SET — video stream may be active!")
            elif vid_state2.get('currentSrc'):
                print(f"  [RESULT] currentSrc: {vid_state2.get('currentSrc')}")
            else:
                print("  [RESULT] No active video source detected")
        except Exception as e:
            print(f"  ERROR checking video post-click: {e}")

        # STEP 10: Console log analysis
        print("\n[STEP 10] Console log analysis ...")
        simli_markers = [
            '[Simli] Avatar stream live',
            '[Simli] Session connected',
            '[Simli] Lip sync track connected',
            'Avatar stream live',
            'Session connected',
            'Lip sync',
        ]
        telnyx_errors = []
        simli_found = []

        for log in console_logs:
            text = log['text']
            for marker in simli_markers:
                if marker.lower() in text.lower():
                    simli_found.append(text)
            if 'telnyx' in text.lower() and 'error' in text.lower():
                telnyx_errors.append(text)

        if simli_found:
            print("  [FOUND] Simli success markers:")
            for m in simli_found:
                print(f"    - {m}")
        else:
            print("  [NOT FOUND] No Simli success markers in console logs")

        if telnyx_errors:
            print("  [FOUND] Telnyx error messages:")
            for e in telnyx_errors:
                print(f"    - {e}")
        else:
            print("  [CLEAN] No Telnyx error messages in console")

        # Print ALL relevant console logs
        print("\n  All relevant console logs (Simli/Telnyx/voice/avatar):")
        relevant = [l for l in console_logs if any(
            kw in l['text'].lower() for kw in ['simli', 'telnyx', 'voice', 'avatar', 'session', 'stream', 'error']
        )]
        if relevant:
            for log in relevant[:50]:  # cap at 50
                print(f"    [{log['type'].upper()}] {log['text'][:300]}")
        else:
            print("    (no relevant logs found)")

        # Check for visible error text on page
        print("\n  Checking for visible error text on page ...")
        try:
            error_text = await page.evaluate("""
                () => {
                    const body = document.body.innerText;
                    const lines = body.split('\\n').filter(l => l.toLowerCase().includes('error') && l.trim().length > 5);
                    return lines.slice(0, 10).join(' | ');
                }
            """)
            if error_text:
                print(f"  Visible error text: {error_text[:500]}")
            else:
                print("  No visible error text on page")
        except Exception as e:
            print(f"  ERROR checking page text: {e}")

        # Final window state check
        print("\n  Final Simli/Telnyx window state ...")
        try:
            final_state_raw = await page.evaluate(JS_CONSOLE_SIMLI.strip().lstrip('() => '))
            final_state = json.loads(final_state_raw)
            print(f"  Final window state: {json.dumps(final_state, indent=4)}")
        except Exception as e:
            print(f"  ERROR: {e}")

        await browser.close()

    print("\n" + "="*60)
    print("TEST COMPLETE")
    print(f"Screenshots: {SCREENSHOTS_DIR}/talkingwidget_01_initial.png")
    print(f"             {SCREENSHOTS_DIR}/talkingwidget_02_post_click.png")
    print("="*60)

if __name__ == '__main__':
    asyncio.run(run_test())
