#!/usr/bin/env python3
import sys
import os
import time
import random
import argparse
import json
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.mouse_button import MouseButton
from selenium.webdriver.common.by import By

def debug(msg: str):
    """Print debug message to stderr"""
    print(f"DEBUG: {msg}", file=sys.stderr)

class ClickExecutor:
    def __init__(self, debug_port: int):
        debug(f"Connecting to Chrome on debug port {debug_port}")
        options = webdriver.ChromeOptions()
        options.add_experimental_option("debuggerAddress", f"127.0.0.1:{debug_port}")
        self.driver = webdriver.Chrome(options=options)
        
        # Load JavaScript files
        js_dir = os.path.join(os.path.dirname(__file__), "..", "js")
        self.js_code = ""
        for filename in ["viewport.js", "mouse.js"]:
            with open(os.path.join(js_dir, filename)) as f:
                self.js_code += f.read() + "\n"
        
        # Get initial window info
        self.window_size = self.driver.get_window_size()
        debug(f"Window size: {json.dumps(self.window_size)}")
        
        self.viewport_size = self.get_viewport_size()
        debug(f"Viewport size: {json.dumps(self.viewport_size)}")
        
        self.scroll_position = self.get_scroll_position()
        debug(f"Scroll position: {json.dumps(self.scroll_position)}")

    def get_viewport_size(self):
        """Get viewport dimensions"""
        return self.driver.execute_script(self.js_code + "; return getViewportInfo();")
        
    def get_scroll_position(self):
        """Get current scroll position"""
        return self.driver.execute_script(self.js_code + "; return getViewportInfo();")
        
    def get_mouse_position(self):
        """Get current mouse coordinates"""
        return self.driver.execute_script(self.js_code + "; return getMousePosition();")

    def scroll_to_target(self, x: int, y: int):
        """Scroll to make target coordinates visible"""
        self.driver.execute_script(
            "window.scrollTo(arguments[0], arguments[1]);", 
            max(0, x - self.viewport_size['width'] // 2),
            max(0, y - self.viewport_size['height'] // 2)
        )
        # Wait for scroll to complete
        time.sleep(0.5)
        self.scroll_position = self.get_scroll_position()
        debug(f"New scroll position: {json.dumps(self.scroll_position)}")

    def get_element_at_position(self, x: int, y: int):
        """Get the element at the specified coordinates"""
        return self.driver.execute_script(self.js_code + "; return getElementAtPosition(arguments[0], arguments[1]);", x, y)

    def execute_click(self, x: int, y: int, double: bool = False, right: bool = False):
        """Execute click operation with improved coordinate handling"""
        try:
            debug(f"Attempting to click at coordinates ({x}, {y})")
            debug(f"Click type: {'double' if double else 'right' if right else 'single'}")
            
            # First scroll to make target visible
            self.scroll_to_target(x, y)
            
            # Get the element at the target position
            element = self.get_element_at_position(x, y)
            if not element:
                debug(f"No element found at coordinates ({x}, {y})")
                return False
                
            # Try clicking the element directly
            try:
                debug("Attempting to click element directly")
                element = self.driver.execute_script("return arguments[0];", element)
                
                actions = ActionChains(self.driver)
                actions.move_to_element(element)
                
                if right:
                    actions.context_click()
                elif double:
                    actions.double_click()
                else:
                    actions.click()
                    
                actions.perform()
                return True
                
            except Exception as e:
                debug(f"Direct element click failed: {str(e)}")
                debug("Falling back to JavaScript click")
                
                # Fallback to JavaScript click
                try:
                    self.driver.execute_script("arguments[0].click();", element)
                    return True
                except Exception as js_error:
                    debug(f"JavaScript click failed: {str(js_error)}")
                    return False
            
        except Exception as e:
            debug(f"Click operation failed with error: {str(e)}")
            return False

def main():
    parser = argparse.ArgumentParser(description='Execute mouse clicks in Chrome')
    parser.add_argument('debug_port', type=int, help='Chrome debug port')
    parser.add_argument('-x', type=int, required=True, help='X coordinate')
    parser.add_argument('-y', type=int, required=True, help='Y coordinate')
    parser.add_argument('--double', action='store_true', help='Perform double click')
    parser.add_argument('--right', action='store_true', help='Perform right click')
    
    args = parser.parse_args()
    
    if args.double and args.right:
        print("Cannot specify both --double and --right", file=sys.stderr)
        sys.exit(1)
        
    try:
        debug("Initializing ClickExecutor")
        executor = ClickExecutor(args.debug_port)
        success = executor.execute_click(args.x, args.y, args.double, args.right)
        sys.exit(0 if success else 1)
        
    except Exception as e:
        debug(f"Fatal error: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()
