#!/usr/bin/env python3
"""
Focused diagnostic #2 for talkingwidget.ai
- Deep shadow DOM inspection to find the internal Telnyx button & events
- Understand the overlay click flow vs internal widget click
- Capture full Simli error
- Determine which event the Telnyx widget ITSELF fires
"""

import asyncio
import json
from playwright.async_api import async_playwright

async def run():
    async with async_playwright() as p:
        print("[DIAG2] Launching with fake mic...")
        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 = []
        errors = []
        page = await context.new_page()
        page.on("console", lambda msg: console_messages.append(f"[{msg.type.upper()}] {msg.text}"))
        page.on("pageerror", lambda err: errors.append(str(err)))

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

        # --- Step A: Full shadow DOM analysis ---
        print("[DIAG2] A: Full shadow DOM analysis...")
        shadow_analysis = await page.evaluate("""() => {
            var el = document.getElementById('ai-voice-engine');
            if (!el || !el.shadowRoot) return {error: 'no shadow root'};
            var sr = el.shadowRoot;

            // Get ALL elements in shadow DOM
            var allEls = Array.from(sr.querySelectorAll('*'));
            var elementList = allEls.map(function(e) {
                return {
                    tag: e.tagName,
                    id: e.id,
                    className: e.className.toString().slice(0, 80),
                    attrs: Array.from(e.attributes).map(function(a) { return a.name + '=' + a.value; }).join(', '),
                    textContent: e.textContent.slice(0, 50).trim()
                };
            });

            // Find buttons specifically
            var buttons = Array.from(sr.querySelectorAll('button, [role=button], [onclick]'));
            var buttonDetails = buttons.map(function(b) {
                return {
                    tag: b.tagName,
                    id: b.id,
                    text: b.textContent.slice(0, 50),
                    type: b.type,
                    className: b.className.toString().slice(0, 80),
                    onclick: b.onclick ? 'has onclick' : 'no onclick'
                };
            });

            // Find audio elements
            var audios = Array.from(sr.querySelectorAll('audio'));
            var audioDetails = audios.map(function(a) {
                return {
                    id: a.id,
                    src: a.src,
                    autoplay: a.autoplay,
                    muted: a.muted,
                    readyState: a.readyState
                };
            });

            return {
                elementCount: allEls.length,
                elements: elementList.slice(0, 50),
                buttons: buttonDetails,
                audios: audioDetails,
                fullHTML: sr.innerHTML.slice(0, 3000)
            };
        }""")
        print(f"[DIAG2] Shadow DOM elements: {shadow_analysis['elementCount']}")
        print(f"[DIAG2] Buttons in shadow: {json.dumps(shadow_analysis.get('buttons', []), indent=2)}")
        print(f"[DIAG2] Audios in shadow: {json.dumps(shadow_analysis.get('audios', []), indent=2)}")
        print(f"[DIAG2] Full shadow HTML (3000):\n{shadow_analysis.get('fullHTML', '')}")

        # --- Step B: Inject comprehensive event spy INSIDE the shadow DOM too ---
        print("[DIAG2] B: Injecting deep spy (shadow DOM + element + doc + win)...")
        await page.evaluate("""() => {
            window._events2 = [];
            var log = function(src, evt, detail) {
                var entry = {src: src, event: evt, time: Date.now(), detail: typeof detail === 'object' ? JSON.stringify(detail) : String(detail || '')};
                window._events2.push(entry);
                console.log('[DEEP-SPY]', src, evt, detail || '');
            };

            var eventList = ['connected','disconnected','call-started','call-ended',
                'telnyx:connected','telnyx:disconnected','telnyx:callStarted','telnyx:callEnded',
                'telnyx-call-connected','telnyx-call-started','telnyx-call-ended','telnyx-call-disconnected',
                'va:start','va:end','stateChange','readyStateChange','open','close',
                'message','error','callStarted','callEnded','change',
                'telnyx:agentSpeaking','telnyx:userSpeaking','telnyx:silenceDetected',
                'click','mousedown','keydown','input',
                'telnyx:callAnswered','telnyx:callHangup','telnyx:callRinging',
                'icegatheringstatechange','iceconnectionstatechange','connectionstatechange',
                'track','negotiationneeded'];

            var el = document.getElementById('ai-voice-engine');

            // Spy on element
            if (el) {
                eventList.forEach(function(evt) {
                    el.addEventListener(evt, function(e) { log('ELEMENT', evt, e.detail); }, {capture: true});
                    el.addEventListener(evt, function(e) { log('ELEMENT-bubble', evt, e.detail); });
                });

                // Spy inside shadow DOM
                if (el.shadowRoot) {
                    var sr = el.shadowRoot;
                    eventList.forEach(function(evt) {
                        sr.addEventListener(evt, function(e) { log('SHADOW', evt, e.detail); }, {capture: true});
                    });
                    // Spy on all buttons inside shadow
                    var btns = sr.querySelectorAll('button, [role=button]');
                    btns.forEach(function(btn, i) {
                        eventList.forEach(function(evt) {
                            btn.addEventListener(evt, function(e) { log('SHADOW-BTN-'+i, evt, e.detail); }, {capture: true});
                        });
                    });
                }
            }

            // Global event intercept
            var origAddEvt = EventTarget.prototype.addEventListener;
            window._interceptedHandlers = {};
            /* Don't override addEventListener - too broad, will break page */

            // Spy on document
            eventList.forEach(function(evt) {
                document.addEventListener(evt, function(e) { log('DOCUMENT', evt, e.detail); }, {capture: true});
            });
            window.addEventListener('message', function(e) { log('WINDOW-postmsg', 'message', e.data); });

            console.log('[DEEP-SPY] Installed on element, shadow, document');
        }""")

        # --- Step C: Understand the overlay click flow ---
        print("[DIAG2] C: Checking what #avatar-idle-overlay click does (JS event handlers)...")
        overlay_info = await page.evaluate("""() => {
            var overlay = document.getElementById('avatar-idle-overlay');
            if (!overlay) return {error: 'no overlay'};

            // Get all event listeners - this only works for ones we can see
            var info = {
                id: overlay.id,
                tagName: overlay.tagName,
                innerHTML: overlay.innerHTML.slice(0, 300),
                style: overlay.style.cssText,
                computedDisplay: window.getComputedStyle(overlay).display,
                computedVisibility: window.getComputedStyle(overlay).visibility,
                onclick: overlay.onclick ? overlay.onclick.toString().slice(0, 200) : null,
            };

            // Check script for how overlay interacts with telnyx widget
            // Look for the click handler in all scripts
            var scripts = Array.from(document.querySelectorAll('script:not([src])'));
            var relevantCode = scripts.map(function(s) {
                var code = s.textContent;
                if (code.includes('avatar-idle-overlay') || code.includes('ai-voice-engine') ||
                    code.includes('va:start') || code.includes('telnyx') || code.includes('simli')) {
                    return code.slice(0, 2000);
                }
                return null;
            }).filter(Boolean);

            return {info: info, relevantScripts: relevantCode};
        }""")
        print(f"[DIAG2] Overlay info: {json.dumps(overlay_info.get('info', {}), indent=2)}")
        print(f"[DIAG2] Relevant scripts ({len(overlay_info.get('relevantScripts', []))}):")
        for i, script in enumerate(overlay_info.get('relevantScripts', [])):
            print(f"  Script {i}: {script[:2000]}")

        # --- Step D: Click the overlay, then click INSIDE shadow DOM button too ---
        print("[DIAG2] D: Clicking #avatar-idle-overlay...")
        await page.click('#avatar-idle-overlay')
        await asyncio.sleep(2)

        # Also try clicking the shadow DOM button
        print("[DIAG2] D2: Trying to click shadow DOM button...")
        shadow_click = await page.evaluate("""() => {
            var el = document.getElementById('ai-voice-engine');
            if (!el || !el.shadowRoot) return 'no shadow';
            var btn = el.shadowRoot.querySelector('button');
            if (btn) {
                btn.click();
                return 'clicked shadow button: ' + btn.textContent.slice(0, 50);
            }
            return 'no button in shadow';
        }""")
        print(f"[DIAG2] Shadow button click: {shadow_click}")

        await asyncio.sleep(10)

        # --- Step E: Capture all events ---
        print("[DIAG2] E: Collecting all events fired...")
        events = await page.evaluate("() => window._events2 || []")
        print(f"[DIAG2] Total events captured: {len(events)}")
        for evt in events:
            print(f"  [{evt['src']}] {evt['event']} | {evt.get('detail', '')[:100]}")

        # --- Step F: Check Telnyx element state after click ---
        print("[DIAG2] F: Telnyx element state after click...")
        telnyx_state = await page.evaluate("""() => {
            var el = document.getElementById('ai-voice-engine');
            if (!el) return {error: 'gone'};

            // Try to read any state properties
            var stateProps = {};
            ['callState', 'connectionState', 'isConnected', 'isActive', 'state', 'status',
             'agentId', 'conversationId', 'sessionId', 'isCallActive', 'isCallStarted'].forEach(function(p) {
                try { stateProps[p] = el[p]; } catch(e) {}
            });

            var attrs = {};
            for (var a of el.attributes) { attrs[a.name] = a.value; }

            // Shadow DOM state after click
            var shadowState = null;
            if (el.shadowRoot) {
                var sr = el.shadowRoot;
                // Look for class changes that indicate state
                var allEls = Array.from(sr.querySelectorAll('*'));
                shadowState = {
                    audioCount: sr.querySelectorAll('audio').length,
                    videoCount: sr.querySelectorAll('video').length,
                    buttonCount: sr.querySelectorAll('button').length,
                    // Get classes of main div to detect state changes
                    mainDivClasses: sr.querySelector('div') ? sr.querySelector('div').className.slice(0, 200) : '',
                    // Try to find any state indicator
                    stateIndicators: allEls.filter(function(e) {
                        return e.className && (e.className.toString().includes('active') ||
                               e.className.toString().includes('call') ||
                               e.className.toString().includes('connect'));
                    }).map(function(e) { return {tag: e.tagName, class: e.className.toString().slice(0, 100)}; })
                };
            }

            return {stateProps, attrs, shadowState};
        }""")
        print(f"[DIAG2] Telnyx state after click: {json.dumps(telnyx_state, indent=2)}")

        # --- Step G: Check Simli specifically ---
        print("[DIAG2] G: Simli state analysis...")
        simli_state = await page.evaluate("""() => {
            // Find simli-related DOM elements
            var simliVid = document.getElementById('simli-vid');
            var simliEls = Array.from(document.querySelectorAll('[id*="simli"], [class*="simli"]'));

            // Check avatar-active-overlay
            var activeOverlay = document.getElementById('avatar-active-overlay');
            var idleOverlay = document.getElementById('avatar-idle-overlay');
            var avatarVid = document.getElementById('avatar-vid');
            var avatarStage = document.getElementById('avatar-stage');

            return {
                simliVid: simliVid ? {
                    display: simliVid.style.display,
                    src: simliVid.src,
                    readyState: simliVid.readyState,
                    paused: simliVid.paused,
                    computedDisplay: window.getComputedStyle(simliVid).display
                } : null,
                simliElCount: simliEls.length,
                simliElDetails: simliEls.map(function(e) { return {tag: e.tagName, id: e.id, class: e.className.toString().slice(0, 60), display: e.style.display}; }),
                activeOverlay: activeOverlay ? {
                    display: activeOverlay.style.display,
                    computedDisplay: window.getComputedStyle(activeOverlay).display,
                    innerHTML: activeOverlay.innerHTML.slice(0, 200)
                } : null,
                idleOverlay: idleOverlay ? {
                    display: idleOverlay.style.display,
                    computedDisplay: window.getComputedStyle(idleOverlay).display
                } : null,
                avatarVid: avatarVid ? {
                    src: avatarVid.src,
                    display: avatarVid.style.display,
                    computedDisplay: window.getComputedStyle(avatarVid).display
                } : null,
                avatarStage: avatarStage ? {
                    className: avatarStage.className,
                    display: avatarStage.style.display
                } : null
            };
        }""")
        print(f"[DIAG2] Simli state: {json.dumps(simli_state, indent=2)}")

        # --- Step H: Read external JS files for event names ---
        print("[DIAG2] H: Checking what external scripts are loaded...")
        scripts_loaded = await page.evaluate("""() => {
            return Array.from(document.querySelectorAll('script[src]')).map(function(s) {
                return {src: s.src, type: s.type, async: s.async, defer: s.defer};
            });
        }""")
        print(f"[DIAG2] External scripts: {json.dumps(scripts_loaded, indent=2)}")

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

        print("\n[DIAG2] Page errors:")
        for err in errors:
            print(f"  ERROR: {err}")

        await browser.close()

        return {
            'shadow_analysis': shadow_analysis,
            'overlay_info': overlay_info,
            'events': events,
            'telnyx_state': telnyx_state,
            'simli_state': simli_state,
            'scripts_loaded': scripts_loaded,
            'console_messages': console_messages
        }


if __name__ == "__main__":
    report = asyncio.run(run())
    with open("/mnt/e/genesis-system/scripts/talkingwidget_diagnostic2_report.json", "w") as f:
        json.dump(report, f, indent=2)
    print("\n[DIAG2] Full report saved to scripts/talkingwidget_diagnostic2_report.json")
