#!/usr/bin/env python3
import contextlib
import json
import pathlib
import socket
import subprocess
import tempfile
import time
import urllib.request
import websocket

ROOT = pathlib.Path(__file__).resolve().parents[1]
GUARD = (ROOT / 'page-guard.js').read_text(encoding='utf-8')

SETUP = r'''(() => {
  document.body.innerHTML = '<video id="movie_player" controls muted style="width:640px;height:360px;background:#111"></video><div id="framework">framework surface</div>';
  window.__errors=[];
  window.__frameworkSignals=0;
  window.__videoClicks=0;
  window.__workerResult=null;
  addEventListener('error', e => __errors.push(String(e.error || e.message)));
  addEventListener('unhandledrejection', e => __errors.push(String(e.reason)));
  const framework = document.getElementById('framework');
  framework.onclick = () => { __frameworkSignals += 1; };
  const video = document.querySelector('video');
  video.addEventListener('click', () => { __videoClicks += 1; });
  try {
    const blob = new Blob(['postMessage("worker-ok")'], {type:'text/javascript'});
    const url = URL.createObjectURL(blob);
    const worker = new Worker(url);
    worker.onmessage = e => { __workerResult=e.data; worker.terminate(); URL.revokeObjectURL(url); };
  } catch (e) { __errors.push(String(e)); }
})();'''


def free_port():
    with socket.socket() as sock:
        sock.bind(('127.0.0.1', 0))
        return sock.getsockname()[1]

class CDP:
    def __init__(self, ws_url):
        self.ws = websocket.create_connection(ws_url, timeout=60, origin='http://localhost')
        self.next_id = 1
    def call(self, method, params=None, timeout=30):
        ident = self.next_id
        self.next_id += 1
        self.ws.send(json.dumps({'id': ident, 'method': method, 'params': params or {}}))
        deadline = time.time() + timeout
        while time.time() < deadline:
            msg = json.loads(self.ws.recv())
            if msg.get('id') == ident:
                if 'error' in msg:
                    raise RuntimeError(msg['error'])
                return msg.get('result', {})
        raise TimeoutError(method)
    def eval(self, expression, user_gesture=False, timeout=30):
        response = self.call('Runtime.evaluate', {
            'expression': expression,
            'returnByValue': True,
            'awaitPromise': True,
            'userGesture': user_gesture,
        }, timeout=timeout)
        if response.get('exceptionDetails'):
            raise RuntimeError(response['exceptionDetails'])
        result = response.get('result', {})
        if result.get('subtype') == 'error':
            raise RuntimeError(result.get('description'))
        return result.get('value')
    def close(self):
        self.ws.close()


def get_page(debug_port):
    endpoint = f'http://127.0.0.1:{debug_port}/json/list'
    for _ in range(200):
        try:
            with urllib.request.urlopen(endpoint, timeout=1) as response:
                targets = json.load(response)
            page = next((t for t in targets if t.get('type') == 'page'), None)
            if page:
                return page
        except Exception:
            pass
        time.sleep(0.1)
    raise RuntimeError('DevTools page unavailable')


def run_context(referrer, expression):
    debug_port = free_port()
    with tempfile.TemporaryDirectory(prefix='grax147-chromium-') as profile:
        cmd = [
            'chromium', '--headless=new', '--no-sandbox', '--disable-gpu', '--no-first-run',
            '--no-default-browser-check', '--disable-background-networking', '--no-proxy-server',
            '--remote-allow-origins=*', '--autoplay-policy=no-user-gesture-required', '--mute-audio',
            f'--remote-debugging-port={debug_port}', f'--user-data-dir={profile}', 'about:blank'
        ]
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        cdp = None
        try:
            page = get_page(debug_port)
            cdp = CDP(page['webSocketDebuggerUrl'])
            cdp.call('Runtime.enable')
            cdp.call('Page.enable')
            prelude = f'''(() => {{
              try {{ Object.defineProperty(document, 'referrer', {{ configurable: true, value: {json.dumps(referrer)} }}); }} catch {{}}
              window.__nativeOpenCalls = [];
              window.__nativeOpenSpy = function nativeOpenSpy(url, target, features) {{
                window.__nativeOpenCalls.push([String(url ?? ''), String(target ?? ''), String(features ?? '')]);
                return {{ native: true, opener: window, location: {{ href: String(url || 'about:blank') }}, close() {{}}, focus() {{}} }};
              }};
              try {{ Object.defineProperty(window, 'open', {{ configurable: true, enumerable: true, writable: true, value: window.__nativeOpenSpy }}); }} catch {{ window.open = window.__nativeOpenSpy; }}
            }})();'''
            cdp.call('Page.addScriptToEvaluateOnNewDocument', {'source': prelude})
            cdp.call('Page.addScriptToEvaluateOnNewDocument', {'source': GUARD})
            cdp.call('Page.reload', {'ignoreCache': True}, timeout=60)
            deadline = time.time() + 20
            while time.time() < deadline:
                try:
                    if cdp.eval("document.readyState === 'complete' && Boolean(globalThis.__graxPageGuardInstalled)"):
                        break
                except Exception:
                    pass
                time.sleep(0.1)
            else:
                raise RuntimeError('Guard did not initialize after about:blank reload')
            cdp.eval(SETUP, timeout=60)
            time.sleep(0.5)
            return cdp.eval(expression, user_gesture=True, timeout=60)
        finally:
            if cdp:
                with contextlib.suppress(Exception): cdp.close()
            proc.terminate()
            with contextlib.suppress(subprocess.TimeoutExpired): proc.wait(timeout=5)
            if proc.poll() is None: proc.kill()


def main():
    trusted = run_context('https://www.youtube.com/watch?v=M7lc1UVf-VE', r'''(async()=>{
      await new Promise(r=>setTimeout(r,300));
      const framework=document.getElementById('framework');
      framework.click();
      const video=document.querySelector('video');
      video.dispatchEvent(new MouseEvent('click',{bubbles:true,cancelable:true}));
      const root=document.createElement('div').attachShadow({mode:'closed'});
      const googleVideoResult=window.open('https://redirector.googlevideo.com/videoplayback?id=147','_blank');
      return {
        installed:Boolean(globalThis.__graxPageGuardInstalled),
        nativeOpenUntouched:window.open===window.__nativeOpenSpy,
        nativeDispatchUntouched:/\[native code\]/.test(Function.prototype.toString.call(Element.prototype.dispatchEvent)),
        nativeAnchorClickUntouched:/\[native code\]/.test(Function.prototype.toString.call(HTMLAnchorElement.prototype.click)),
        closedShadowPreserved:root.mode==='closed',
        frameworkSignals:window.__frameworkSignals,
        videoClicks:window.__videoClicks,
        workerResult:window.__workerResult,
        googleVideoDestinationNative:Boolean(googleVideoResult?.native),
        nativeOpenCalls:window.__nativeOpenCalls,
        errors:window.__errors
      };
    })()''')
    trusted_failures=[]
    for key in ['installed','nativeOpenUntouched','nativeDispatchUntouched','nativeAnchorClickUntouched','closedShadowPreserved','googleVideoDestinationNative']:
        if trusted.get(key) is not True: trusted_failures.append(key)
    if trusted.get('frameworkSignals') != 1: trusted_failures.append('frameworkSignals')
    if trusted.get('videoClicks') != 1: trusted_failures.append('videoClicks')
    if trusted.get('workerResult') != 'worker-ok': trusted_failures.append('workerResult')
    if trusted.get('errors'): trusted_failures.append('errors')
    if len(trusted.get('nativeOpenCalls', [])) != 1: trusted_failures.append('nativeOpenCalls')
    if trusted_failures:
        raise AssertionError('Trusted YouTube runtime failures: '+', '.join(trusted_failures)+'\n'+json.dumps(trusted,indent=2))

    grok = run_context('https://grok.com/', r'''(async()=>{
      await new Promise(r=>setTimeout(r,300));
      const framework=document.getElementById('framework');
      framework.click();
      const root=document.createElement('div').attachShadow({mode:'closed'});
      const xResult=window.open('https://x.com/i/api/graphql/test','_blank');
      return {
        installed:Boolean(globalThis.__graxPageGuardInstalled),
        nativeOpenUntouched:window.open===window.__nativeOpenSpy,
        nativeDispatchUntouched:/\[native code\]/.test(Function.prototype.toString.call(Element.prototype.dispatchEvent)),
        nativeAnchorClickUntouched:/\[native code\]/.test(Function.prototype.toString.call(HTMLAnchorElement.prototype.click)),
        closedShadowPreserved:root.mode==='closed',
        frameworkSignals:window.__frameworkSignals,
        workerResult:window.__workerResult,
        xDestinationNative:Boolean(xResult?.native),
        nativeOpenCalls:window.__nativeOpenCalls,
        errors:window.__errors
      };
    })()''')
    grok_failures=[]
    for key in ['installed','nativeOpenUntouched','nativeDispatchUntouched','nativeAnchorClickUntouched','closedShadowPreserved','xDestinationNative']:
        if grok.get(key) is not True: grok_failures.append(key)
    if grok.get('frameworkSignals') != 1: grok_failures.append('frameworkSignals')
    if grok.get('workerResult') != 'worker-ok': grok_failures.append('workerResult')
    if grok.get('errors'): grok_failures.append('errors')
    if len(grok.get('nativeOpenCalls', [])) != 1: grok_failures.append('nativeOpenCalls')
    if grok_failures:
        raise AssertionError('Trusted Grok/X runtime failures: '+', '.join(grok_failures)+'\n'+json.dumps(grok,indent=2))

    streaming = run_context('https://filma24.stream/movie', r'''(async()=>{
      await new Promise(r=>setTimeout(r,300));
      const video=document.querySelector('video');
      let playback=0;
      video.addEventListener('click',()=>{
        playback+=1;
        window.open('https://rcdn-web.com/ad?clickid=147','_blank');
      });
      video.dispatchEvent(new MouseEvent('click',{bubbles:true,cancelable:true}));
      const adResult=window.open('https://rcdn-web.com/ad?zoneid=147','_blank');
      const trustedResult=window.open('https://www.youtube.com/watch?v=M7lc1UVf-VE','_blank');
      const adAnchor=document.createElement('a');
      adAnchor.href='https://rcdn-web.com/landing?clickid=147';
      adAnchor.target='_blank';
      const adAnchorResult=adAnchor.click();
      const youtubeAnchor=document.createElement('a');
      youtubeAnchor.href='https://www.youtube.com/watch?v=M7lc1UVf-VE';
      youtubeAnchor.target='_blank';
      youtubeAnchor.addEventListener('click', e=>e.preventDefault());
      const youtubeAnchorResult=youtubeAnchor.click();
      return {
        installed:Boolean(globalThis.__graxPageGuardInstalled),
        openFrozen:Object.getOwnPropertyDescriptor(window,'open')?.configurable===false && Object.getOwnPropertyDescriptor(window,'open')?.writable===false,
        playback,
        adWindowInert:Boolean(adResult && adResult.location && adResult.location.href==='about:blank'),
        trustedWindowNative:Boolean(trustedResult?.native),
        adAnchorBlocked:adAnchorResult===false,
        trustedAnchorNative:youtubeAnchorResult!==false || youtubeAnchor.dispatchEvent(new MouseEvent('click',{cancelable:true}))===false,
        workerResult:window.__workerResult,
        nativeOpenCalls:window.__nativeOpenCalls,
        errors:window.__errors
      };
    })()''')
    streaming_failures=[]
    for key in ['installed','openFrozen','adWindowInert','trustedWindowNative','adAnchorBlocked','trustedAnchorNative']:
        if streaming.get(key) is not True: streaming_failures.append(key)
    if streaming.get('playback') != 1: streaming_failures.append('playback')
    if streaming.get('workerResult') != 'worker-ok': streaming_failures.append('workerResult')
    if streaming.get('errors'): streaming_failures.append('errors')
    calls=streaming.get('nativeOpenCalls',[])
    if len(calls) != 1 or 'youtube.com' not in calls[0][0]: streaming_failures.append('destinationCallIsolation')
    if streaming_failures:
        raise AssertionError('Streaming containment failures: '+', '.join(streaming_failures)+'\n'+json.dumps(streaming,indent=2))

    print(json.dumps({
        'chromium': subprocess.check_output(['chromium','--version'], text=True).strip(),
        'youtubeTrustedApplicationContext': trusted,
        'grokXTrustedApplicationContext': grok,
        'streamingDestinationContainment': streaming,
        'externalYouTubeNavigation': 'blocked by managed test environment; trusted context exercised with Chromium document referrer origin simulation'
    }, indent=2))

if __name__ == '__main__':
    main()
