#!/usr/bin/env python3
# Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyleft: this program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# Hermes Node Protocol
# Copyright (c) 2026 Stefy (nextime) Lanza <stefy@nexlab.net>
# All rights reserved.
# 
# This software is released under the MIT License with a copyleft clause.
# See the LICENSE file for full terms.
"""
Browser Control Example Usage

Demonstrates how to use the browser control capability
via Hermes Node Protocol.

This example shows:
1. Launching browser in different modes
2. Creating contexts (incognito, persistent, named)
3. High-level commands (navigate, click, fill, screenshot)
4. Playwright API access
5. CDP (Chrome DevTools Protocol) access

Author: Lisa (Hermes AI)
Date: 2026-04-29
"""

import asyncio
import json
import base64
from pathlib import Path


class BrowserControlExample:
    """Example browser control client"""
    
    def __init__(self, ws):
        """
        Args:
            ws: WebSocket connection to Hermes Gateway
        """
        self.ws = ws
        self.cmd_counter = 0
        
    def _next_id(self):
        """Generate unique command ID"""
        self.cmd_counter += 1
        return f"cmd_{self.cmd_counter}"
    
    async def send_command(self, layer, command, page_id=None, params=None):
        """
        Send browser control command and wait for response
        
        Args:
            layer: Command layer (high_level, playwright, cdp)
            command: Command name
            page_id: Page identifier (optional)
            params: Command parameters (optional)
            
        Returns:
            Response dict
        """
        cmd_id = self._next_id()
        
        msg = {
            "type": "browser_control",
            "id": cmd_id,
            "layer": layer,
            "command": command,
        }
        
        if page_id:
            msg["page_id"] = page_id
        if params:
            msg["params"] = params
        
        # Send command
        await self.ws.send(json.dumps(msg))
        print(f"→ Sent: {layer}/{command} (id={cmd_id})")
        
        # Wait for response
        while True:
            response = await self.ws.recv()
            resp_data = json.loads(response)
            
            if resp_data.get("type") == "browser_control_response" and resp_data.get("id") == cmd_id:
                print(f"← Received: {resp_data.get('result')}")
                return resp_data
    
    async def example_basic_navigation(self):
        """Example 1: Basic navigation and screenshot"""
        print("\n=== Example 1: Basic Navigation ===\n")
        
        # Launch browser in headless mode
        result = await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        print(f"Browser launched: {result.get('browser_version')}")
        
        # Create new page
        result = await self.send_command(
            "high_level", "new_page"
        )
        page_id = result.get("page_id")
        print(f"Page created: {page_id}")
        
        # Navigate to URL
        result = await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://example.com"}
        )
        print(f"Navigated to: {result.get('url')}")
        print(f"Page title: {result.get('title')}")
        
        # Take screenshot
        result = await self.send_command(
            "high_level", "screenshot",
            page_id=page_id,
            params={"full_page": True}
        )
        
        if result.get("result") == "ok":
            # Save screenshot
            screenshot_data = base64.b64decode(result.get("screenshot"))
            screenshot_path = Path("/tmp/example_screenshot.png")
            screenshot_path.write_bytes(screenshot_data)
            print(f"Screenshot saved to: {screenshot_path}")
        
        # Close browser
        await self.send_command("high_level", "close")
        print("Browser closed")
    
    async def example_form_interaction(self):
        """Example 2: Form filling and interaction"""
        print("\n=== Example 2: Form Interaction ===\n")
        
        # Launch browser
        await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        
        # Create page
        result = await self.send_command("high_level", "new_page")
        page_id = result.get("page_id")
        
        # Navigate to form page
        await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://httpbin.org/forms/post"}
        )
        
        # Fill form fields
        await self.send_command(
            "high_level", "fill",
            page_id=page_id,
            params={"selector": "input[name='custname']", "value": "John Doe"}
        )
        
        await self.send_command(
            "high_level", "fill",
            page_id=page_id,
            params={"selector": "input[name='custtel']", "value": "555-1234"}
        )
        
        # Click submit button
        await self.send_command(
            "high_level", "click",
            page_id=page_id,
            params={"selector": "button[type='submit']"}
        )
        
        # Wait for navigation
        await asyncio.sleep(2)
        
        # Get page content
        result = await self.send_command(
            "high_level", "get_content",
            page_id=page_id
        )
        print(f"Page content length: {len(result.get('content', ''))} bytes")
        
        # Close
        await self.send_command("high_level", "close")
    
    async def example_multiple_contexts(self):
        """Example 3: Multiple contexts (simulate multiple users)"""
        print("\n=== Example 3: Multiple Contexts ===\n")
        
        # Launch browser
        await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        
        # Create context for user 1
        await self.send_command(
            "high_level", "create_context",
            params={"config": {"name": "user1", "incognito": True}}
        )
        
        # Create context for user 2
        await self.send_command(
            "high_level", "create_context",
            params={"config": {"name": "user2", "incognito": True}}
        )
        
        # Create page in user1 context
        result = await self.send_command(
            "high_level", "new_page",
            params={"context_name": "user1"}
        )
        page1_id = result.get("page_id")
        
        # Create page in user2 context
        result = await self.send_command(
            "high_level", "new_page",
            params={"context_name": "user2"}
        )
        page2_id = result.get("page_id")
        
        # Navigate both pages to different URLs
        await self.send_command(
            "high_level", "navigate",
            page_id=page1_id,
            params={"url": "https://example.com"}
        )
        
        await self.send_command(
            "high_level", "navigate",
            page_id=page2_id,
            params={"url": "https://httpbin.org"}
        )
        
        # List all pages
        result = await self.send_command("high_level", "list_pages")
        print(f"Open pages: {len(result.get('pages', []))}")
        for page in result.get("pages", []):
            print(f"  - {page['page_id']}: {page['url']}")
        
        # List all contexts
        result = await self.send_command("high_level", "list_contexts")
        print(f"Open contexts: {len(result.get('contexts', []))}")
        for ctx in result.get("contexts", []):
            print(f"  - {ctx['name']}: {ctx['page_count']} pages")
        
        # Close
        await self.send_command("high_level", "close")
    
    async def example_javascript_execution(self):
        """Example 4: JavaScript execution"""
        print("\n=== Example 4: JavaScript Execution ===\n")
        
        # Launch and create page
        await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        result = await self.send_command("high_level", "new_page")
        page_id = result.get("page_id")
        
        # Navigate
        await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://example.com"}
        )
        
        # Execute simple script
        result = await self.send_command(
            "high_level", "execute_script",
            page_id=page_id,
            params={"script": "document.title"}
        )
        print(f"Page title via script: {result.get('result')}")
        
        # Evaluate expression
        result = await self.send_command(
            "high_level", "evaluate",
            page_id=page_id,
            params={"expression": "() => document.querySelectorAll('a').length"}
        )
        print(f"Number of links: {result.get('result')}")
        
        # Execute complex script
        script = """
        () => {
            return {
                url: window.location.href,
                title: document.title,
                links: document.querySelectorAll('a').length,
                images: document.querySelectorAll('img').length,
                viewport: {
                    width: window.innerWidth,
                    height: window.innerHeight
                }
            };
        }
        """
        result = await self.send_command(
            "high_level", "evaluate",
            page_id=page_id,
            params={"expression": script}
        )
        print(f"Page info: {json.dumps(result.get('result'), indent=2)}")
        
        # Close
        await self.send_command("high_level", "close")
    
    async def example_playwright_api(self):
        """Example 5: Direct Playwright API access"""
        print("\n=== Example 5: Playwright API ===\n")
        
        # Launch and create page
        await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        result = await self.send_command("high_level", "new_page")
        page_id = result.get("page_id")
        
        # Navigate
        await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://example.com"}
        )
        
        # Use Playwright locator API
        result = await self.send_command(
            "playwright", "locator",
            page_id=page_id,
            params={"args": ["h1"], "kwargs": {}}
        )
        print(f"Locator result: {result.get('result')}")
        
        # Use get_by_text
        result = await self.send_command(
            "playwright", "get_by_text",
            page_id=page_id,
            params={"args": ["Example Domain"], "kwargs": {}}
        )
        print(f"get_by_text result: {result.get('result')}")
        
        # Close
        await self.send_command("high_level", "close")
    
    async def example_cdp_access(self):
        """Example 6: Chrome DevTools Protocol access"""
        print("\n=== Example 6: CDP Access ===\n")
        
        # Launch and create page
        await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        result = await self.send_command("high_level", "new_page")
        page_id = result.get("page_id")
        
        # Enable network tracking via CDP
        result = await self.send_command(
            "cdp", "Network.enable",
            page_id=page_id,
            params={}
        )
        print(f"Network tracking enabled: {result.get('result')}")
        
        # Navigate
        await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://example.com"}
        )
        
        # Get performance metrics via CDP
        result = await self.send_command(
            "cdp", "Performance.getMetrics",
            page_id=page_id,
            params={}
        )
        print(f"Performance metrics: {result.get('result')}")
        
        # Evaluate via CDP
        result = await self.send_command(
            "cdp", "Runtime.evaluate",
            page_id=page_id,
            params={
                "expression": "navigator.userAgent",
                "returnByValue": True
            }
        )
        print(f"User agent: {result.get('result')}")
        
        # Close
        await self.send_command("high_level", "close")
    
    async def example_headed_mode(self):
        """Example 7: Headed mode (visible browser)"""
        print("\n=== Example 7: Headed Mode ===\n")
        
        # Launch in headed mode
        result = await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headed"}}
        )
        print(f"Browser launched in headed mode: {result.get('browser_version')}")
        
        # Create page
        result = await self.send_command("high_level", "new_page")
        page_id = result.get("page_id")
        
        # Navigate
        await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://example.com"}
        )
        
        print("Browser is visible. Waiting 5 seconds...")
        await asyncio.sleep(5)
        
        # Close
        await self.send_command("high_level", "close")
    
    async def example_persistent_context(self):
        """Example 8: Persistent context (saved profile)"""
        print("\n=== Example 8: Persistent Context ===\n")
        
        # Launch browser
        await self.send_command(
            "high_level", "launch",
            params={"config": {"mode": "headless"}}
        )
        
        # Create persistent context
        profile_dir = "/tmp/browser_profile"
        await self.send_command(
            "high_level", "create_context",
            params={
                "config": {
                    "name": "persistent",
                    "persistent": True,
                    "user_data_dir": profile_dir
                }
            }
        )
        print(f"Created persistent context at: {profile_dir}")
        
        # Create page in persistent context
        result = await self.send_command(
            "high_level", "new_page",
            params={"context_name": "persistent"}
        )
        page_id = result.get("page_id")
        
        # Navigate and interact
        await self.send_command(
            "high_level", "navigate",
            page_id=page_id,
            params={"url": "https://example.com"}
        )
        
        # Set localStorage via JavaScript
        await self.send_command(
            "high_level", "execute_script",
            page_id=page_id,
            params={"script": "localStorage.setItem('test', 'persistent_data')"}
        )
        print("Set localStorage item")
        
        # Close
        await self.send_command("high_level", "close")
        print(f"Profile saved to: {profile_dir}")


async def main():
    """Main entry point"""
    # This is a demonstration of the API
    # In real usage, you would connect to the Hermes Gateway WebSocket
    
    print("Browser Control Example Usage")
    print("=" * 50)
    print()
    print("This script demonstrates the browser control API.")
    print("To use it, you need to:")
    print("1. Start Hermes Gateway")
    print("2. Start Hermes Node Agent with browser control enabled")
    print("3. Connect to the gateway WebSocket")
    print()
    print("Example connection code:")
    print()
    print("import websockets")
    print("async with websockets.connect('ws://gateway:8765') as ws:")
    print("    example = BrowserControlExample(ws)")
    print("    await example.example_basic_navigation()")
    print()
    print("See BROWSER_PROTOCOL.md for full protocol documentation.")


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