package daemon

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/launcher"
	"github.com/go-rod/rod/lib/proto"
)

const Version = "2.2.1-contrast-detection-fix"

// Daemon is the main server that manages browser connections
type Daemon struct {
	browser     *rod.Browser
	tabs        map[string]*rod.Page
	iframePages map[string]*rod.Page    // Maps tab ID to iframe page context
	currentTab  string                  // ID of the current/last used tab
	tabHistory  []string                // Stack of tab IDs in order of activation (LIFO)
	consoleLogs map[string][]ConsoleLog // Maps tab ID to console logs
	debug       bool                    // Enable debug logging
	mu          sync.Mutex
	server      *http.Server
}

// ConsoleLog represents a console log entry
type ConsoleLog struct {
	Level     string    `json:"level"`     // log, warn, error, info, debug
	Message   string    `json:"message"`   // The log message
	Timestamp time.Time `json:"timestamp"` // When the log occurred
	Source    string    `json:"source"`    // Source location if available
}

// Command represents a command sent from the client to the daemon
type Command struct {
	Action string            `json:"action"`
	Params map[string]string `json:"params"`
}

// Response represents a response from the daemon to the client
type Response struct {
	Success bool        `json:"success"`
	Data    interface{} `json:"data,omitempty"`
	Error   string      `json:"error,omitempty"`
}

// checkChromeRunning checks if Chrome is running on the debug port
func checkChromeRunning(port int) bool {
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%d", port), 2*time.Second)
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

// checkChromeDevTools checks if Chrome DevTools protocol is responding
func checkChromeDevTools(port int) bool {
	resp, err := http.Get(fmt.Sprintf("http://localhost:%d/json/version", port))
	if err != nil {
		return false
	}
	defer resp.Body.Close()
	return resp.StatusCode == 200
}

// debugLog logs a message only if debug mode is enabled
func (d *Daemon) debugLog(format string, args ...interface{}) {
	if d.debug {
		log.Printf("[DEBUG] "+format, args...)
	}
}

// NewDaemon creates a new daemon instance
func NewDaemon(host string, port int, debug bool) (*Daemon, error) {
	if debug {
		log.Printf("[DEBUG] Creating new daemon on %s:%d", host, port)
	}

	// Check if Chrome is running on the debug port
	chromePort := 9222 // Default Chrome debug port

	if debug {
		log.Printf("[DEBUG] Checking if Chrome is running on port %d", chromePort)
	}

	if !checkChromeRunning(chromePort) {
		return nil, fmt.Errorf("Chromium is not running with remote debugging enabled on port %d.\n\nTo start Chromium with remote debugging:\n  chromium --remote-debugging-port=%d --user-data-dir=/tmp/chromium-debug &\n  # or\n  google-chrome --remote-debugging-port=%d --user-data-dir=/tmp/chrome-debug &\n\nNote: The --user-data-dir flag is required to avoid conflicts with existing browser instances.", chromePort, chromePort, chromePort)
	}

	// Check if Chromium DevTools protocol is responding
	if !checkChromeDevTools(chromePort) {
		return nil, fmt.Errorf("Something is listening on port %d but it's not Chromium DevTools protocol.\n\nThis might be:\n1. Chromium running without --remote-debugging-port=%d\n2. Another application using port %d\n\nTry stopping the process on port %d and starting Chromium with:\n  chromium --remote-debugging-port=%d --user-data-dir=/tmp/chromium-debug &", chromePort, chromePort, chromePort, chromePort, chromePort)
	}

	// Connect to the existing browser instance
	u := launcher.MustResolveURL("")
	browser := rod.New().ControlURL(u)

	err := browser.Connect()
	if err != nil {
		return nil, fmt.Errorf("Chromium DevTools is responding on port %d but rod connection failed: %w\n\nThis is unexpected. Try restarting Chromium with:\n  chromium --remote-debugging-port=%d --user-data-dir=/tmp/chromium-debug &", chromePort, err, chromePort)
	}

	if debug {
		log.Printf("[DEBUG] Successfully connected to browser via rod")
	}

	daemon := &Daemon{
		browser:     browser,
		tabs:        make(map[string]*rod.Page),
		iframePages: make(map[string]*rod.Page),
		tabHistory:  make([]string, 0),
		consoleLogs: make(map[string][]ConsoleLog),
		debug:       debug,
	}

	daemon.debugLog("Daemon struct initialized")

	// Create HTTP server
	daemon.debugLog("Setting up HTTP server")
	mux := http.NewServeMux()
	mux.HandleFunc("/command", daemon.handleCommand)
	mux.HandleFunc("/status", daemon.handleStatus)
	mux.HandleFunc("/upload", daemon.handleFileUpload)
	mux.HandleFunc("/download", daemon.handleFileDownload)

	daemon.server = &http.Server{
		Addr:    fmt.Sprintf("%s:%d", host, port),
		Handler: mux,
	}

	daemon.debugLog("HTTP server configured on %s:%d", host, port)

	return daemon, nil
}

// Start starts the daemon server
func (d *Daemon) Start() error {
	log.Printf("Starting daemon server on %s", d.server.Addr)
	d.debugLog("About to call ListenAndServe()")
	err := d.server.ListenAndServe()
	d.debugLog("ListenAndServe() returned with error: %v", err)
	return err
}

// Stop stops the daemon server
func (d *Daemon) Stop() error {
	log.Println("Stopping daemon server")
	return d.server.Close()
}

// handleStatus handles status requests
func (d *Daemon) handleStatus(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	d.mu.Lock()
	tabCount := len(d.tabs)
	tabs := make(map[string]string)
	currentTab := d.currentTab
	tabHistory := make([]string, len(d.tabHistory))
	copy(tabHistory, d.tabHistory)

	// Get info about each tab
	for id, page := range d.tabs {
		try := func() string {
			info, err := page.Info()
			if err != nil {
				return "<unavailable>"
			}
			return info.URL
		}
		tabs[id] = try()
	}
	d.mu.Unlock()

	response := Response{
		Success: true,
		Data: map[string]interface{}{
			"status":      "running",
			"tab_count":   tabCount,
			"tabs":        tabs,
			"current_tab": currentTab,
			"tab_history": tabHistory,
		},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// handleCommand handles command requests
func (d *Daemon) handleCommand(w http.ResponseWriter, r *http.Request) {
	d.debugLog("Received HTTP request: %s %s", r.Method, r.URL.Path)

	if r.Method != http.MethodPost {
		d.debugLog("Invalid method: %s", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var cmd Command
	err := json.NewDecoder(r.Body).Decode(&cmd)
	if err != nil {
		d.debugLog("Failed to decode JSON: %v", err)
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	d.debugLog("Processing command: %s with params: %+v", cmd.Action, cmd.Params)

	var response Response

	switch cmd.Action {
	case "version":
		response = Response{
			Success: true,
			Data:    Version,
		}

	case "open-tab":
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		tabID, err := d.openTab(timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: tabID}
		}

	case "load-url":
		tabID := cmd.Params["tab"]
		url := cmd.Params["url"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.loadURL(tabID, url, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "fill-form":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		value := cmd.Params["value"]

		// Parse timeouts
		selectionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				selectionTimeout = parsedTimeout
			}
		}

		actionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["action-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				actionTimeout = parsedTimeout
			}
		}

		err := d.fillFormField(tabID, selector, value, selectionTimeout, actionTimeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "upload-file":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		filePath := cmd.Params["file"]

		// Parse timeouts
		selectionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				selectionTimeout = parsedTimeout
			}
		}

		actionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["action-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				actionTimeout = parsedTimeout
			}
		}

		err := d.uploadFile(tabID, selector, filePath, selectionTimeout, actionTimeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "submit-form":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]

		// Parse timeouts
		selectionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				selectionTimeout = parsedTimeout
			}
		}

		actionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["action-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				actionTimeout = parsedTimeout
			}
		}

		err := d.submitForm(tabID, selector, selectionTimeout, actionTimeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "get-source":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		source, err := d.getPageSource(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: source}
		}

	case "get-element":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]

		// Parse timeouts
		selectionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				selectionTimeout = parsedTimeout
			}
		}

		html, err := d.getElementHTML(tabID, selector, selectionTimeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: html}
		}

	case "close-tab":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.closeTab(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "wait-navigation":
		tabID := cmd.Params["tab"]
		timeout := 30 // Default timeout
		if timeoutStr, ok := cmd.Params["timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}
		err := d.waitNavigation(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "click-element":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]

		// Parse timeouts
		selectionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				selectionTimeout = parsedTimeout
			}
		}

		actionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["action-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				actionTimeout = parsedTimeout
			}
		}

		err := d.clickElement(tabID, selector, selectionTimeout, actionTimeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "select-element":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		value := cmd.Params["value"]

		// Parse timeouts
		selectionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				selectionTimeout = parsedTimeout
			}
		}

		actionTimeout := 30 // Default: 30 seconds
		if timeoutStr, ok := cmd.Params["action-timeout"]; ok {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				actionTimeout = parsedTimeout
			}
		}

		err := d.selectElement(tabID, selector, value, selectionTimeout, actionTimeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "eval-js":
		tabID := cmd.Params["tab"]
		jsCode := cmd.Params["code"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.evalJS(tabID, jsCode, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "switch-iframe":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.switchToIframe(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "switch-main":
		tabID := cmd.Params["tab"]

		err := d.switchToMain(tabID)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "screenshot":
		tabID := cmd.Params["tab"]
		outputPath := cmd.Params["output"]
		fullPageStr := cmd.Params["full-page"]
		zoomLevelStr := cmd.Params["zoom_level"]  // Optional: zoom level (e.g., "2.0")
		viewportWidthStr := cmd.Params["width"]   // Optional: viewport width
		viewportHeightStr := cmd.Params["height"] // Optional: viewport height
		timeoutStr := cmd.Params["timeout"]

		// Parse full-page flag
		fullPage := false
		if fullPageStr == "true" {
			fullPage = true
		}

		// Parse zoom level
		var zoomLevel float64
		if zoomLevelStr != "" {
			if parsed, err := strconv.ParseFloat(zoomLevelStr, 64); err == nil && parsed > 0 {
				zoomLevel = parsed
			}
		}

		// Parse viewport dimensions
		var viewportWidth, viewportHeight int
		if viewportWidthStr != "" {
			if parsed, err := strconv.Atoi(viewportWidthStr); err == nil && parsed > 0 {
				viewportWidth = parsed
			}
		}
		if viewportHeightStr != "" {
			if parsed, err := strconv.Atoi(viewportHeightStr); err == nil && parsed > 0 {
				viewportHeight = parsed
			}
		}

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.takeScreenshotEnhanced(tabID, outputPath, fullPage, zoomLevel, viewportWidth, viewportHeight, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "console-logs":
		tabID := cmd.Params["tab"]
		clearStr := cmd.Params["clear"]

		// Parse clear flag
		clear := false
		if clearStr == "true" {
			clear = true
		}

		logs, err := d.getConsoleLogs(tabID, clear)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: logs}
		}

	case "console-command":
		tabID := cmd.Params["tab"]
		command := cmd.Params["command"]
		injectLibrary := cmd.Params["inject_library"] // Optional: library URL or name
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Inject library if specified
		if injectLibrary != "" {
			err := d.injectLibrary(tabID, injectLibrary, timeout)
			if err != nil {
				response = Response{Success: false, Error: fmt.Sprintf("failed to inject library: %v", err)}
				break
			}
		}

		result, err := d.executeConsoleCommand(tabID, command, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "check-element":
		d.debugLog("Processing check-element command")
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		checkType := cmd.Params["type"] // exists, visible, enabled, focused, selected
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.checkElement(tabID, selector, checkType, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "get-element-attributes":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		attributes := cmd.Params["attributes"] // comma-separated list or "all"
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.getElementAttributes(tabID, selector, attributes, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "count-elements":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		count, err := d.countElements(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: count}
		}

	case "extract-multiple":
		tabID := cmd.Params["tab"]
		selectors := cmd.Params["selectors"] // JSON array of selectors
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.extractMultiple(tabID, selectors, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "extract-links":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]        // Optional: filter links by container selector
		hrefPattern := cmd.Params["href-pattern"] // Optional: regex pattern for href
		textPattern := cmd.Params["text-pattern"] // Optional: regex pattern for link text
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.extractLinks(tabID, selector, hrefPattern, textPattern, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "extract-table":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		includeHeaders := cmd.Params["include-headers"] == "true"
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.extractTable(tabID, selector, includeHeaders, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "extract-text":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		pattern := cmd.Params["pattern"]  // Optional: regex pattern to match within text
		extractType := cmd.Params["type"] // text, innerText, textContent (default: textContent)
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.extractText(tabID, selector, pattern, extractType, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "analyze-form":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.analyzeForm(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "interact-multiple":
		tabID := cmd.Params["tab"]
		interactionsJSON := cmd.Params["interactions"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.interactMultiple(tabID, interactionsJSON, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "fill-form-bulk":
		tabID := cmd.Params["tab"]
		formSelector := cmd.Params["form-selector"]
		fieldsJSON := cmd.Params["fields"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.fillFormBulk(tabID, formSelector, fieldsJSON, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "get-page-info":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.getPageInfo(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "get-viewport-info":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.getViewportInfo(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "get-performance":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.getPerformance(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "check-content":
		tabID := cmd.Params["tab"]
		contentType := cmd.Params["type"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.checkContent(tabID, contentType, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "screenshot-element":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		outputPath := cmd.Params["output"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.screenshotElement(tabID, selector, outputPath, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "screenshot-enhanced":
		tabID := cmd.Params["tab"]
		outputPath := cmd.Params["output"]
		fullPageStr := cmd.Params["full-page"]
		timeoutStr := cmd.Params["timeout"]

		// Parse full-page flag
		fullPage := false
		if fullPageStr == "true" {
			fullPage = true
		}

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.screenshotEnhanced(tabID, outputPath, fullPage, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "bulk-files":
		operationType := cmd.Params["operation"] // "upload" or "download"
		filesJSON := cmd.Params["files"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds for bulk operations)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.bulkFiles(operationType, filesJSON, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "manage-files":
		operation := cmd.Params["operation"] // "cleanup", "list", "info"
		pattern := cmd.Params["pattern"]     // file pattern for cleanup/list
		maxAge := cmd.Params["max-age"]      // max age in hours for cleanup

		result, err := d.manageFiles(operation, pattern, maxAge)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	// Accessibility tree commands
	case "get-accessibility-tree":
		tabID := cmd.Params["tab"]
		depth := cmd.Params["depth"]
		includeContrastStr := cmd.Params["include_contrast"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Parse depth (optional)
		var depthInt *int
		if depth != "" {
			if parsedDepth, err := strconv.Atoi(depth); err == nil && parsedDepth >= 0 {
				depthInt = &parsedDepth
			}
		}

		// Parse include_contrast flag
		includeContrast := false
		if includeContrastStr == "true" {
			includeContrast = true
		}

		result, err := d.getAccessibilityTreeWithContrast(tabID, depthInt, includeContrast, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "get-partial-accessibility-tree":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		fetchRelatives := cmd.Params["fetch-relatives"] // "true" or "false"
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Parse fetchRelatives (default to true)
		fetchRel := true
		if fetchRelatives == "false" {
			fetchRel = false
		}

		result, err := d.getPartialAccessibilityTree(tabID, selector, fetchRel, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "query-accessibility-tree":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		accessibleName := cmd.Params["accessible-name"]
		role := cmd.Params["role"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.queryAccessibilityTree(tabID, selector, accessibleName, role, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "disable-cache":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.setCacheDisabled(tabID, true, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "enable-cache":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.setCacheDisabled(tabID, false, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "clear-cache":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.clearBrowserCache(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "clear-all-site-data":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.clearAllSiteData(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "clear-cookies":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.clearCookies(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "clear-storage":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds if not specified)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.clearStorage(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "drag-and-drop":
		tabID := cmd.Params["tab"]
		sourceSelector := cmd.Params["source"]
		targetSelector := cmd.Params["target"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if sourceSelector == "" {
			response = Response{Success: false, Error: "source selector is required"}
			break
		}
		if targetSelector == "" {
			response = Response{Success: false, Error: "target selector is required"}
			break
		}

		err := d.dragAndDrop(tabID, sourceSelector, targetSelector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "drag-and-drop-coordinates":
		tabID := cmd.Params["tab"]
		sourceSelector := cmd.Params["source"]
		targetXStr := cmd.Params["target-x"]
		targetYStr := cmd.Params["target-y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if sourceSelector == "" {
			response = Response{Success: false, Error: "source selector is required"}
			break
		}
		if targetXStr == "" || targetYStr == "" {
			response = Response{Success: false, Error: "target-x and target-y coordinates are required"}
			break
		}

		targetX, err := strconv.Atoi(targetXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid target-x coordinate"}
			break
		}
		targetY, err := strconv.Atoi(targetYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid target-y coordinate"}
			break
		}

		err = d.dragAndDropToCoordinates(tabID, sourceSelector, targetX, targetY, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "drag-and-drop-offset":
		tabID := cmd.Params["tab"]
		sourceSelector := cmd.Params["source"]
		offsetXStr := cmd.Params["offset-x"]
		offsetYStr := cmd.Params["offset-y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if sourceSelector == "" {
			response = Response{Success: false, Error: "source selector is required"}
			break
		}
		if offsetXStr == "" || offsetYStr == "" {
			response = Response{Success: false, Error: "offset-x and offset-y are required"}
			break
		}

		offsetX, err := strconv.Atoi(offsetXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid offset-x value"}
			break
		}
		offsetY, err := strconv.Atoi(offsetYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid offset-y value"}
			break
		}

		err = d.dragAndDropByOffset(tabID, sourceSelector, offsetX, offsetY, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "right-click":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" {
			response = Response{Success: false, Error: "selector is required"}
			break
		}

		err := d.rightClick(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "double-click":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" {
			response = Response{Success: false, Error: "selector is required"}
			break
		}

		err := d.doubleClick(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "middle-click":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" {
			response = Response{Success: false, Error: "selector is required"}
			break
		}

		err := d.middleClick(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "hover":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" {
			response = Response{Success: false, Error: "selector is required"}
			break
		}

		err := d.hover(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "mouse-move":
		tabID := cmd.Params["tab"]
		xStr := cmd.Params["x"]
		yStr := cmd.Params["y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if xStr == "" || yStr == "" {
			response = Response{Success: false, Error: "x and y coordinates are required"}
			break
		}

		x, err := strconv.Atoi(xStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid x coordinate"}
			break
		}
		y, err := strconv.Atoi(yStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid y coordinate"}
			break
		}

		err = d.mouseMove(tabID, x, y, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "scroll-wheel":
		tabID := cmd.Params["tab"]
		xStr := cmd.Params["x"]
		yStr := cmd.Params["y"]
		deltaXStr := cmd.Params["delta-x"]
		deltaYStr := cmd.Params["delta-y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if xStr == "" || yStr == "" || deltaXStr == "" || deltaYStr == "" {
			response = Response{Success: false, Error: "x, y, delta-x, and delta-y are required"}
			break
		}

		x, err := strconv.Atoi(xStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid x coordinate"}
			break
		}
		y, err := strconv.Atoi(yStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid y coordinate"}
			break
		}
		deltaX, err := strconv.Atoi(deltaXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid delta-x value"}
			break
		}
		deltaY, err := strconv.Atoi(deltaYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid delta-y value"}
			break
		}

		err = d.scrollWheel(tabID, x, y, deltaX, deltaY, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "key-combination":
		tabID := cmd.Params["tab"]
		keys := cmd.Params["keys"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if keys == "" {
			response = Response{Success: false, Error: "keys parameter is required"}
			break
		}

		err := d.keyCombination(tabID, keys, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "special-key":
		tabID := cmd.Params["tab"]
		key := cmd.Params["key"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if key == "" {
			response = Response{Success: false, Error: "key parameter is required"}
			break
		}

		err := d.specialKey(tabID, key, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "modifier-click":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		modifiers := cmd.Params["modifiers"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" {
			response = Response{Success: false, Error: "selector is required"}
			break
		}
		if modifiers == "" {
			response = Response{Success: false, Error: "modifiers parameter is required"}
			break
		}

		err := d.modifierClick(tabID, selector, modifiers, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "touch-tap":
		tabID := cmd.Params["tab"]
		xStr := cmd.Params["x"]
		yStr := cmd.Params["y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if xStr == "" || yStr == "" {
			response = Response{Success: false, Error: "x and y coordinates are required"}
			break
		}

		x, err := strconv.Atoi(xStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid x coordinate"}
			break
		}
		y, err := strconv.Atoi(yStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid y coordinate"}
			break
		}

		err = d.touchTap(tabID, x, y, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "touch-long-press":
		tabID := cmd.Params["tab"]
		xStr := cmd.Params["x"]
		yStr := cmd.Params["y"]
		durationStr := cmd.Params["duration"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		duration := 1000 // Default 1000ms
		if durationStr != "" {
			if parsedDuration, err := strconv.Atoi(durationStr); err == nil && parsedDuration > 0 {
				duration = parsedDuration
			}
		}

		if xStr == "" || yStr == "" {
			response = Response{Success: false, Error: "x and y coordinates are required"}
			break
		}

		x, err := strconv.Atoi(xStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid x coordinate"}
			break
		}
		y, err := strconv.Atoi(yStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid y coordinate"}
			break
		}

		err = d.touchLongPress(tabID, x, y, duration, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "touch-swipe":
		tabID := cmd.Params["tab"]
		startXStr := cmd.Params["start-x"]
		startYStr := cmd.Params["start-y"]
		endXStr := cmd.Params["end-x"]
		endYStr := cmd.Params["end-y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if startXStr == "" || startYStr == "" || endXStr == "" || endYStr == "" {
			response = Response{Success: false, Error: "start-x, start-y, end-x, and end-y are required"}
			break
		}

		startX, err := strconv.Atoi(startXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid start-x coordinate"}
			break
		}
		startY, err := strconv.Atoi(startYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid start-y coordinate"}
			break
		}
		endX, err := strconv.Atoi(endXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid end-x coordinate"}
			break
		}
		endY, err := strconv.Atoi(endYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid end-y coordinate"}
			break
		}

		err = d.touchSwipe(tabID, startX, startY, endX, endY, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "pinch-zoom":
		tabID := cmd.Params["tab"]
		centerXStr := cmd.Params["center-x"]
		centerYStr := cmd.Params["center-y"]
		scaleStr := cmd.Params["scale"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if centerXStr == "" || centerYStr == "" || scaleStr == "" {
			response = Response{Success: false, Error: "center-x, center-y, and scale are required"}
			break
		}

		centerX, err := strconv.Atoi(centerXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid center-x coordinate"}
			break
		}
		centerY, err := strconv.Atoi(centerYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid center-y coordinate"}
			break
		}
		scale, err := strconv.ParseFloat(scaleStr, 64)
		if err != nil {
			response = Response{Success: false, Error: "invalid scale value"}
			break
		}

		err = d.pinchZoom(tabID, centerX, centerY, scale, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "scroll-element":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		deltaXStr := cmd.Params["delta-x"]
		deltaYStr := cmd.Params["delta-y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" || deltaXStr == "" || deltaYStr == "" {
			response = Response{Success: false, Error: "selector, delta-x, and delta-y are required"}
			break
		}

		deltaX, err := strconv.Atoi(deltaXStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid delta-x value"}
			break
		}
		deltaY, err := strconv.Atoi(deltaYStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid delta-y value"}
			break
		}

		err = d.scrollElement(tabID, selector, deltaX, deltaY, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "scroll-to-coordinates":
		tabID := cmd.Params["tab"]
		xStr := cmd.Params["x"]
		yStr := cmd.Params["y"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if xStr == "" || yStr == "" {
			response = Response{Success: false, Error: "x and y coordinates are required"}
			break
		}

		x, err := strconv.Atoi(xStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid x coordinate"}
			break
		}
		y, err := strconv.Atoi(yStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid y coordinate"}
			break
		}

		err = d.scrollToCoordinates(tabID, x, y, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "select-text":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"]
		startStr := cmd.Params["start"]
		endStr := cmd.Params["end"]
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		if selector == "" || startStr == "" || endStr == "" {
			response = Response{Success: false, Error: "selector, start, and end are required"}
			break
		}

		start, err := strconv.Atoi(startStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid start index"}
			break
		}
		end, err := strconv.Atoi(endStr)
		if err != nil {
			response = Response{Success: false, Error: "invalid end index"}
			break
		}

		err = d.selectText(tabID, selector, start, end, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "select-all-text":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"] // Optional - if empty, selects all text on page
		timeoutStr := cmd.Params["timeout"]
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.selectAllText(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true}
		}

	case "inject-axe":
		tabID := cmd.Params["tab"]
		axeVersion := cmd.Params["version"] // Optional: specific axe-core version
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds for library injection)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		err := d.injectAxeCore(tabID, axeVersion, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: "axe-core injected successfully"}
		}

	case "run-axe":
		tabID := cmd.Params["tab"]
		optionsJSON := cmd.Params["options"] // Optional: JSON string with axe.run() options
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds for comprehensive testing)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Parse options if provided
		var options map[string]interface{}
		if optionsJSON != "" {
			err := json.Unmarshal([]byte(optionsJSON), &options)
			if err != nil {
				response = Response{Success: false, Error: fmt.Sprintf("invalid options JSON: %v", err)}
				break
			}
		}

		result, err := d.runAxeCore(tabID, options, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "check-contrast":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"] // Optional: CSS selector for specific elements
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.checkContrast(tabID, selector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "check-gradient-contrast":
		tabID := cmd.Params["tab"]
		selector := cmd.Params["selector"] // Required: CSS selector for element with gradient background
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Selector is required for gradient contrast check
		if selector == "" {
			response = Response{Success: false, Error: "selector parameter is required for gradient contrast check"}
		} else {
			result, err := d.checkGradientContrast(tabID, selector, timeout)
			if err != nil {
				response = Response{Success: false, Error: err.Error()}
			} else {
				response = Response{Success: true, Data: result}
			}
		}

	case "validate-media":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.validateMedia(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "test-hover-focus":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.testHoverFocusContent(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "detect-text-in-images":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds for OCR processing)
		timeout := 30
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.detectTextInImages(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "check-cross-page-consistency":
		tabID := cmd.Params["tab"]
		urlsStr := cmd.Params["urls"]
		timeoutStr := cmd.Params["timeout"]

		// Parse URLs (comma-separated)
		if urlsStr == "" {
			response = Response{Success: false, Error: "urls parameter is required"}
			break
		}
		urls := strings.Split(urlsStr, ",")
		for i := range urls {
			urls[i] = strings.TrimSpace(urls[i])
		}

		// Parse timeout (default to 10 seconds per page)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.checkCrossPageConsistency(tabID, urls, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "detect-animation-flash":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.detectAnimationFlash(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "analyze-enhanced-accessibility":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		result, err := d.analyzeEnhancedAccessibility(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "test-keyboard":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 15 seconds for comprehensive testing)
		timeout := 15
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Always use real keyboard simulation for accurate :focus-visible detection
		result, err := d.testKeyboardNavigationWithRealKeys(tabID, timeout)

		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "test-zoom":
		tabID := cmd.Params["tab"]
		zoomLevelsStr := cmd.Params["zoom_levels"] // Optional: comma-separated zoom levels
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds per zoom level)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Parse zoom levels if provided
		var zoomLevels []float64
		if zoomLevelsStr != "" {
			levels := strings.Split(zoomLevelsStr, ",")
			for _, level := range levels {
				if zoom, err := strconv.ParseFloat(strings.TrimSpace(level), 64); err == nil && zoom > 0 {
					zoomLevels = append(zoomLevels, zoom)
				}
			}
		}

		result, err := d.testZoom(tabID, zoomLevels, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "test-reflow":
		tabID := cmd.Params["tab"]
		widthsStr := cmd.Params["widths"] // Optional: comma-separated widths
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds per width)
		timeout := 10
		if timeoutStr != "" {
			if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
				timeout = parsedTimeout
			}
		}

		// Parse widths if provided
		var widths []int
		if widthsStr != "" {
			widthStrs := strings.Split(widthsStr, ",")
			for _, widthStr := range widthStrs {
				if width, err := strconv.Atoi(strings.TrimSpace(widthStr)); err == nil && width > 0 {
					widths = append(widths, width)
				}
			}
		}

		result, err := d.testReflow(tabID, widths, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "page-accessibility-report":
		tabID := cmd.Params["tab"]
		testsStr := cmd.Params["tests"]
		standard := cmd.Params["standard"]
		includeScreenshots := cmd.Params["include_screenshots"] == "true"
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds)
		timeout := 30
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		// Parse tests array
		var tests []string
		if testsStr != "" {
			tests = strings.Split(testsStr, ",")
		}

		result, err := d.getPageAccessibilityReport(tabID, tests, standard, includeScreenshots, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "contrast-audit":
		tabID := cmd.Params["tab"]
		prioritySelectorsStr := cmd.Params["priority_selectors"]
		threshold := cmd.Params["threshold"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		// Parse priority selectors
		var prioritySelectors []string
		if prioritySelectorsStr != "" {
			prioritySelectors = strings.Split(prioritySelectorsStr, ",")
		}

		result, err := d.getContrastAudit(tabID, prioritySelectors, threshold, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "keyboard-audit":
		tabID := cmd.Params["tab"]
		checkFocusIndicators := cmd.Params["check_focus_indicators"] == "true"
		checkTabOrder := cmd.Params["check_tab_order"] == "true"
		checkKeyboardTraps := cmd.Params["check_keyboard_traps"] == "true"
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 15 seconds)
		timeout := 15
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		// Always use real keyboard simulation for accurate :focus-visible detection
		result, err := d.getKeyboardAudit(tabID, checkFocusIndicators, checkTabOrder, checkKeyboardTraps, true, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "form-accessibility-audit":
		tabID := cmd.Params["tab"]
		formSelector := cmd.Params["form_selector"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 10 seconds)
		timeout := 10
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		result, err := d.getFormAccessibilityAudit(tabID, formSelector, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "extract-divi-structure":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds)
		timeout := 30
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		result, err := d.extractDiviStructure(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "extract-divi-images":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds)
		timeout := 30
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		result, err := d.extractDiviImages(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	case "extract-divi-content":
		tabID := cmd.Params["tab"]
		timeoutStr := cmd.Params["timeout"]

		// Parse timeout (default to 30 seconds)
		timeout := 30
		if timeoutStr != "" {
			if t, err := strconv.Atoi(timeoutStr); err == nil {
				timeout = t
			}
		}

		result, err := d.extractDiviContent(tabID, timeout)
		if err != nil {
			response = Response{Success: false, Error: err.Error()}
		} else {
			response = Response{Success: true, Data: result}
		}

	default:
		d.debugLog("Unknown action: %s", cmd.Action)
		response = Response{Success: false, Error: "Unknown action"}
	}

	d.debugLog("Command %s completed, sending response: success=%v", cmd.Action, response.Success)
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
	d.debugLog("Response sent for command: %s", cmd.Action)
}

// openTab opens a new tab and returns its ID
func (d *Daemon) openTab(timeout int) (string, error) {
	d.debugLog("Opening new tab with timeout: %d", timeout)
	d.mu.Lock()
	defer d.mu.Unlock()

	// Create a context with timeout if specified
	if timeout > 0 {
		d.debugLog("Using timeout context: %d seconds", timeout)
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan struct {
			page  *rod.Page
			tabID string
			err   error
		}, 1)

		// Execute the tab creation in a goroutine
		go func() {
			page, err := d.browser.Page(proto.TargetCreateTarget{URL: "about:blank"})
			var tabID string
			if err == nil {
				tabID = string(page.TargetID)
			}
			done <- struct {
				page  *rod.Page
				tabID string
				err   error
			}{page, tabID, err}
		}()

		// Wait for either completion or timeout
		select {
		case res := <-done:
			if res.err != nil {
				return "", fmt.Errorf("failed to create new tab: %w", res.err)
			}

			// Store the tab and update history
			d.tabs[res.tabID] = res.page
			d.tabHistory = append(d.tabHistory, res.tabID)
			d.currentTab = res.tabID

			// Set up console logging for this tab
			d.setupConsoleLogging(res.tabID, res.page)

			return res.tabID, nil
		case <-ctx.Done():
			return "", fmt.Errorf("opening tab timed out after %d seconds", timeout)
		}
	} else {
		// No timeout
		page, err := d.browser.Page(proto.TargetCreateTarget{URL: "about:blank"})
		if err != nil {
			return "", fmt.Errorf("failed to create new tab: %w", err)
		}

		// Use the page ID as the tab ID
		tabID := string(page.TargetID)
		d.tabs[tabID] = page

		// Add to tab history stack and set as current tab
		d.tabHistory = append(d.tabHistory, tabID)
		d.currentTab = tabID

		// Set up console logging for this tab
		d.setupConsoleLogging(tabID, page)

		return tabID, nil
	}
}

// getTabID returns the tab ID to use, falling back to the current tab if none is provided
func (d *Daemon) getTabID(tabID string) (string, error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	// If no tab ID is provided, use the current tab
	if tabID == "" {
		if d.currentTab == "" {
			return "", fmt.Errorf("no current tab available, please open a tab first")
		}
		return d.currentTab, nil
	}

	// Otherwise, use the provided tab ID
	return tabID, nil
}

// updateTabHistory updates the tab history stack when a tab is activated
func (d *Daemon) updateTabHistory(tabID string) {
	// Set as current tab
	d.currentTab = tabID

	// Remove the tab from history if it's already there
	for i, id := range d.tabHistory {
		if id == tabID {
			// Remove this tab from history
			d.tabHistory = append(d.tabHistory[:i], d.tabHistory[i+1:]...)
			break
		}
	}

	// Add the tab to the end of history (most recent)
	d.tabHistory = append(d.tabHistory, tabID)
}

// findPageByID finds a page by its ID without updating the current tab or cache
func (d *Daemon) findPageByID(tabID string) (*rod.Page, error) {
	// If not in memory, try to get the page from the browser
	pages, err := d.browser.Pages()
	if err != nil {
		return nil, fmt.Errorf("failed to get browser pages: %w", err)
	}

	// Find the page with the matching ID
	for _, p := range pages {
		if string(p.TargetID) == tabID {
			// IMPORTANT: Pages retrieved from browser.Pages() may not be fully initialized.
			// The rod library has a known issue where internal maps (like the helper map) are nil
			// when pages are retrieved this way, causing "assignment to entry in nil map" panics.
			// See: https://github.com/go-rod/rod/issues/331
			//
			// To work around this, we need to ensure the page is properly initialized.
			// We use a recover block to catch any panics during initialization attempts.
			defer func() {
				if r := recover(); r != nil {
					d.debugLog("Recovered from panic while initializing page %s: %v", tabID, r)
				}
			}()

			// Try to initialize the page by calling Info() which sets up internal state
			_, err := p.Info()
			if err != nil {
				d.debugLog("Warning: found page %s but failed to initialize it: %v", tabID, err)
				// Return error instead of continuing, as the page is likely not usable
				return nil, fmt.Errorf("found page %s but failed to initialize: %w", tabID, err)
			}

			return p, nil
		}
	}

	return nil, fmt.Errorf("tab not found: %s", tabID)
}

// getTab returns a tab by its ID, checking for iframe context first
func (d *Daemon) getTab(tabID string) (*rod.Page, error) {
	// Get the tab ID to use (may be the current tab)
	actualTabID, err := d.getTabID(tabID)
	if err != nil {
		return nil, err
	}

	d.mu.Lock()
	defer d.mu.Unlock()

	// First check if we have an iframe context for this tab
	if iframePage, exists := d.iframePages[actualTabID]; exists {
		// Update tab history and current tab
		d.updateTabHistory(actualTabID)
		return iframePage, nil
	}

	// Check in-memory cache for main page
	page, exists := d.tabs[actualTabID]
	if exists {
		// Update tab history and current tab
		d.updateTabHistory(actualTabID)
		return page, nil
	}

	// If not in memory, try to find it
	// Note: This can happen if the daemon was restarted but the browser is still running,
	// or if a tab was created outside of the daemon. Pages retrieved this way may have
	// initialization issues (see findPageByID for details).
	page, err = d.findPageByID(actualTabID)
	if err != nil {
		return nil, err
	}

	// If found, cache it for future use and set up console logging
	if page != nil {
		d.tabs[actualTabID] = page
		// Set up console logging for this tab since it wasn't created through openTab
		d.setupConsoleLogging(actualTabID, page)
		// Update tab history and current tab
		d.updateTabHistory(actualTabID)
		d.debugLog("Found and cached existing tab %s from browser", actualTabID)
		return page, nil
	}

	return nil, fmt.Errorf("tab not found: %s", actualTabID)
}

// closeTab closes a tab by its ID
func (d *Daemon) closeTab(tabID string, timeout int) error {
	// Get the tab ID to use (may be the current tab)
	actualTabID, err := d.getTabID(tabID)
	if err != nil {
		return err
	}

	// First remove from our internal map to avoid future references
	d.mu.Lock()
	page, exists := d.tabs[actualTabID]
	delete(d.tabs, actualTabID)

	// Remove the tab from history
	for i, id := range d.tabHistory {
		if id == actualTabID {
			// Remove this tab from history
			d.tabHistory = append(d.tabHistory[:i], d.tabHistory[i+1:]...)
			break
		}
	}

	// If we closed the current tab, set it to the previous tab in history
	if d.currentTab == actualTabID {
		if len(d.tabHistory) > 0 {
			// Set current tab to the most recent tab in history
			d.currentTab = d.tabHistory[len(d.tabHistory)-1]
		} else {
			// No tabs left in history, clear the current tab
			d.currentTab = ""
		}
	}
	d.mu.Unlock()

	// If the page doesn't exist in our cache, try to find it
	if !exists {
		var err error
		page, err = d.findPageByID(actualTabID)
		if err != nil {
			// If we can't find the page, it might already be closed
			return nil
		}
	}

	if timeout > 0 {
		// Use timeout for closing the tab
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the close in a goroutine
		go func() {
			err := page.Close()
			done <- err
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				// Log the error but don't return it, as we've already removed it from our map
				fmt.Printf("Warning: failed to close tab %s: %v\n", actualTabID, err)
			}
			return nil
		case <-ctx.Done():
			return fmt.Errorf("closing tab timed out after %d seconds", timeout)
		}
	} else {
		// No timeout - try to close the page, but don't fail if it's already closed
		err = page.Close()
		if err != nil {
			// Log the error but don't return it, as we've already removed it from our map
			fmt.Printf("Warning: failed to close tab %s: %v\n", actualTabID, err)
		}

		return nil
	}
}

// loadURL loads a URL in a tab
func (d *Daemon) loadURL(tabID, url string, timeout int) error {
	d.debugLog("Loading URL: %s in tab: %s with timeout: %d", url, tabID, timeout)
	page, err := d.getTab(tabID)
	if err != nil {
		d.debugLog("Failed to get tab %s: %v", tabID, err)
		return err
	}
	d.debugLog("Got tab %s, starting navigation", tabID)

	// Ensure page is fully initialized before using in goroutine
	// This prevents "assignment to entry in nil map" panics in rod library
	// See: https://github.com/go-rod/rod/issues/331
	_, err = page.Info()
	if err != nil {
		d.debugLog("Warning: failed to initialize page %s: %v", tabID, err)
		// Continue anyway, as the page might still work
	}

	if timeout > 0 {
		// Use timeout for the URL loading
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the navigation in a goroutine
		go func() {
			defer func() {
				if r := recover(); r != nil {
					done <- fmt.Errorf("navigation panicked: %v", r)
				}
			}()

			err := page.Navigate(url)
			if err != nil {
				done <- fmt.Errorf("failed to navigate to URL: %w", err)
				return
			}

			// Wait for the page to be loaded
			err = page.WaitLoad()
			if err != nil {
				done <- fmt.Errorf("failed to wait for page load: %w", err)
				return
			}

			done <- nil
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			return err
		case <-ctx.Done():
			return fmt.Errorf("loading URL timed out after %d seconds", timeout)
		}
	} else {
		// No timeout
		err = page.Navigate(url)
		if err != nil {
			return fmt.Errorf("failed to navigate to URL: %w", err)
		}

		// Wait for the page to be loaded
		err = page.WaitLoad()
		if err != nil {
			return fmt.Errorf("failed to wait for page load: %w", err)
		}

		return nil
	}
}

// isPageStable checks if a page is stable and not currently loading
func (d *Daemon) isPageStable(page *rod.Page) (bool, error) {
	// Check if page is loading
	result, err := page.Eval(`() => document.readyState === 'complete'`)
	if err != nil {
		return false, err
	}
	if result == nil {
		return false, fmt.Errorf("page stability check returned nil result")
	}

	isComplete := result.Value.Bool()

	if !isComplete {
		return false, nil
	}

	// Additional check: ensure no pending network requests
	// This is a simple heuristic - if the page has been stable for a short time
	err = page.WaitStable(500 * time.Millisecond)
	if err != nil {
		return false, nil // Page is not stable
	}

	return true, nil
}

// detectNavigationInProgress monitors the page for a short period to detect if navigation starts
func (d *Daemon) detectNavigationInProgress(page *rod.Page, monitorDuration time.Duration) (bool, error) {
	// Get current URL and readyState
	currentURL, err := page.Eval(`() => window.location.href`)
	if err != nil {
		return false, err
	}
	if currentURL == nil {
		return false, fmt.Errorf("failed to get current URL: nil result")
	}

	currentReadyState, err := page.Eval(`() => document.readyState`)
	if err != nil {
		return false, err
	}
	if currentReadyState == nil {
		return false, fmt.Errorf("failed to get current readyState: nil result")
	}

	startURL := currentURL.Value.Str()
	startReadyState := currentReadyState.Value.Str()

	// Monitor for changes over the specified duration
	ctx, cancel := context.WithTimeout(context.Background(), monitorDuration)
	defer cancel()

	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			// No navigation detected during monitoring period
			return false, nil
		case <-ticker.C:
			// Check if URL or readyState changed
			newURL, err := page.Eval(`() => window.location.href`)
			if err != nil || newURL == nil {
				continue // Ignore errors during monitoring
			}

			newReadyState, err := page.Eval(`() => document.readyState`)
			if err != nil || newReadyState == nil {
				continue // Ignore errors during monitoring
			}

			if newURL.Value.Str() != startURL {
				// URL changed, navigation is happening
				return true, nil
			}

			if newReadyState.Value.Str() != startReadyState && newReadyState.Value.Str() == "loading" {
				// Page started loading
				return true, nil
			}
		}
	}
}

// waitNavigation waits for a navigation event to happen
func (d *Daemon) waitNavigation(tabID string, timeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// First, check if the page is already stable and loaded
	// If so, we don't need to wait for navigation
	isStable, err := d.isPageStable(page)
	if err == nil && isStable {
		// Page is already stable, no navigation happening
		return nil
	}

	// Check if navigation is actually in progress by monitoring for a short period
	navigationDetected, err := d.detectNavigationInProgress(page, 2*time.Second)
	if err != nil {
		return fmt.Errorf("failed to detect navigation state: %w", err)
	}

	if !navigationDetected {
		// No navigation detected, check if page is stable now
		isStable, err := d.isPageStable(page)
		if err == nil && isStable {
			return nil
		}
		// If we can't determine stability, proceed with waiting
	}

	// Navigation is in progress, wait for it to complete
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Wait for navigation with timeout
	done := make(chan error, 1)
	go func() {
		defer func() {
			if r := recover(); r != nil {
				done <- fmt.Errorf("navigation wait panicked: %v", r)
			}
		}()

		// Wait for navigation event
		page.WaitNavigation(proto.PageLifecycleEventNameLoad)()

		// Wait for the page to be fully loaded
		err := page.WaitLoad()
		done <- err
	}()

	select {
	case err := <-done:
		if err != nil {
			return fmt.Errorf("navigation wait failed: %w", err)
		}
		return nil
	case <-ctx.Done():
		// Timeout occurred, check if page is now stable
		isStable, err := d.isPageStable(page)
		if err == nil && isStable {
			// Page is stable, consider navigation complete
			return nil
		}
		return fmt.Errorf("navigation wait timed out after %d seconds", timeout)
	}
}

// getPageSource returns the entire source code of a page
func (d *Daemon) getPageSource(tabID string, timeout int) (string, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return "", err
	}

	if timeout > 0 {
		// Use timeout for getting page source
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan struct {
			html string
			err  error
		}, 1)

		// Execute the HTML retrieval in a goroutine
		go func() {
			html, err := page.HTML()
			done <- struct {
				html string
				err  error
			}{html, err}
		}()

		// Wait for either completion or timeout
		select {
		case res := <-done:
			if res.err != nil {
				return "", fmt.Errorf("failed to get page HTML: %w", res.err)
			}
			return res.html, nil
		case <-ctx.Done():
			return "", fmt.Errorf("getting page source timed out after %d seconds", timeout)
		}
	} else {
		// No timeout
		html, err := page.HTML()
		if err != nil {
			return "", fmt.Errorf("failed to get page HTML: %w", err)
		}

		return html, nil
	}
}

// getElementHTML returns the HTML of an element at the specified selector
func (d *Daemon) getElementHTML(tabID, selector string, selectionTimeout int) (string, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return "", err
	}

	// Find the element with optional timeout
	var element *rod.Element
	if selectionTimeout > 0 {
		// Use timeout if specified
		element, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
		if err != nil {
			return "", fmt.Errorf("failed to find element (timeout after %ds): %w", selectionTimeout, err)
		}
	} else {
		// No timeout
		element, err = page.Element(selector)
		if err != nil {
			return "", fmt.Errorf("failed to find element: %w", err)
		}
	}

	// Get the HTML of the element
	html, err := element.HTML()
	if err != nil {
		return "", fmt.Errorf("failed to get element HTML: %w", err)
	}

	return html, nil
}

// fillFormField fills a form field with the specified value
func (d *Daemon) fillFormField(tabID, selector, value string, selectionTimeout, actionTimeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element with optional timeout
	var element *rod.Element
	if selectionTimeout > 0 {
		// Use timeout if specified
		element, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element (timeout after %ds): %w", selectionTimeout, err)
		}
	} else {
		// No timeout
		element, err = page.Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element: %w", err)
		}
	}

	// Get the element type
	tagName, err := element.Eval(`() => this.tagName.toLowerCase()`)
	if err != nil {
		return fmt.Errorf("failed to get element type: %w", err)
	}

	// Get the element type attribute
	inputType, err := element.Eval(`() => this.type ? this.type.toLowerCase() : ''`)
	if err != nil {
		return fmt.Errorf("failed to get element type attribute: %w", err)
	}
	if tagName == nil || inputType == nil {
		return fmt.Errorf("failed to get element properties: nil result")
	}

	// Handle different input types
	tagNameStr := tagName.Value.String()
	typeStr := inputType.Value.String()

	// Handle checkbox and radio inputs
	if tagNameStr == "input" && (typeStr == "checkbox" || typeStr == "radio") {
		// Convert value to boolean
		checked := false
		if value == "true" || value == "1" || value == "yes" || value == "on" || value == "checked" {
			checked = true
		}

		// Set the checked state with optional timeout
		if actionTimeout > 0 {
			// Use timeout for the action
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(actionTimeout)*time.Second)
			defer cancel()

			// Create a channel to signal completion
			done := make(chan error, 1)

			// Execute the action in a goroutine
			go func() {
				_, err := element.Eval(fmt.Sprintf(`() => { this.checked = %t; return true; }`, checked))
				done <- err
			}()

			// Wait for either completion or timeout
			select {
			case err := <-done:
				if err != nil {
					return fmt.Errorf("failed to set checkbox state: %w", err)
				}
			case <-ctx.Done():
				return fmt.Errorf("setting checkbox state timed out after %d seconds", actionTimeout)
			}

			// Create a channel for the event trigger
			done = make(chan error, 1)

			// Trigger change event with timeout
			go func() {
				_, err := element.Eval(`() => {
					const event = new Event('change', { bubbles: true });
					this.dispatchEvent(event);
					return true;
				}`)
				done <- err
			}()

			// Wait for either completion or timeout
			select {
			case err := <-done:
				if err != nil {
					return fmt.Errorf("failed to trigger change event: %w", err)
				}
			case <-ctx.Done():
				return fmt.Errorf("triggering change event timed out after %d seconds", actionTimeout)
			}
		} else {
			// No timeout
			_, err := element.Eval(fmt.Sprintf(`() => { this.checked = %t; return true; }`, checked))
			if err != nil {
				return fmt.Errorf("failed to set checkbox state: %w", err)
			}

			// Trigger change event
			_, err = element.Eval(`() => {
				const event = new Event('change', { bubbles: true });
				this.dispatchEvent(event);
				return true;
			}`)
			if err != nil {
				return fmt.Errorf("failed to trigger change event: %w", err)
			}
		}

		return nil
	}

	// For regular text inputs
	if actionTimeout > 0 {
		// Use timeout for the action
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(actionTimeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Clear the field first with timeout
		go func() {
			_ = element.SelectAllText()
			err := element.Input("")
			done <- err
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to clear field: %w", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("clearing field timed out after %d seconds", actionTimeout)
		}

		// Create a channel for the input action
		done = make(chan error, 1)

		// Input the value with timeout
		go func() {
			err := element.Input(value)
			done <- err
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to input value: %w", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("inputting value timed out after %d seconds", actionTimeout)
		}
	} else {
		// No timeout
		// Clear the field first
		_ = element.SelectAllText()
		err = element.Input("")
		if err != nil {
			return fmt.Errorf("failed to clear field: %w", err)
		}

		// Input the value
		err = element.Input(value)
		if err != nil {
			return fmt.Errorf("failed to input value: %w", err)
		}
	}

	return nil
}

// uploadFile uploads a file to a file input element
func (d *Daemon) uploadFile(tabID, selector, filePath string, selectionTimeout, actionTimeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element with optional timeout
	var element *rod.Element
	if selectionTimeout > 0 {
		// Use timeout if specified
		element, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find file input element (timeout after %ds): %w", selectionTimeout, err)
		}
	} else {
		// No timeout
		element, err = page.Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find file input element: %w", err)
		}
	}

	// Set the file with optional timeout
	if actionTimeout > 0 {
		// Use timeout for the action
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(actionTimeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the action in a goroutine
		go func() {
			err := element.SetFiles([]string{filePath})
			done <- err
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to set file: %w", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("setting file timed out after %d seconds", actionTimeout)
		}
	} else {
		// No timeout
		err = element.SetFiles([]string{filePath})
		if err != nil {
			return fmt.Errorf("failed to set file: %w", err)
		}
	}

	return nil
}

// submitForm submits a form
func (d *Daemon) submitForm(tabID, selector string, selectionTimeout, actionTimeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element with optional timeout
	var form *rod.Element
	if selectionTimeout > 0 {
		// Use timeout if specified
		form, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find form element (timeout after %ds): %w", selectionTimeout, err)
		}
	} else {
		// No timeout
		form, err = page.Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find form element: %w", err)
		}
	}

	// Get the current URL to detect navigation
	currentURL := page.MustInfo().URL

	// Create a context for navigation timeout
	var ctx context.Context
	var cancel context.CancelFunc

	// Submit the form with optional timeout
	if actionTimeout > 0 {
		// Use timeout for the action
		submitCtx, submitCancel := context.WithTimeout(context.Background(), time.Duration(actionTimeout)*time.Second)
		defer submitCancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the action in a goroutine
		go func() {
			_, err := form.Eval(`() => { try { this.submit(); return true; } catch(e) { return false; } }`)
			done <- err
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				// Log the error but continue
				fmt.Printf("Warning: error during form submission: %v\n", err)
			}
		case <-submitCtx.Done():
			return fmt.Errorf("form submission timed out after %d seconds", actionTimeout)
		}

		// Wait for navigation to complete (with the same timeout)
		ctx, cancel = context.WithTimeout(context.Background(), time.Duration(actionTimeout)*time.Second)
	} else {
		// No timeout for submission
		try := func() (bool, error) {
			_, err := form.Eval(`() => { try { this.submit(); return true; } catch(e) { return false; } }`)
			return err == nil, err
		}

		// Try to submit the form, but don't fail if it's already been submitted
		_, err = try()
		if err != nil {
			// Log the error but continue
			fmt.Printf("Warning: error during form submission: %v\n", err)
		}

		// Wait for navigation to complete (with default timeout)
		ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
	}
	defer cancel()

	// Wait for the page to navigate away from the current URL
	waitNav := func() error {
		for {
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(100 * time.Millisecond):
				// Check if the page has navigated
				try := func() (string, error) {
					info, err := page.Info()
					if err != nil {
						return "", err
					}
					return info.URL, nil
				}

				newURL, err := try()
				if err != nil {
					// Page might be navigating, wait a bit more
					continue
				}

				if newURL != currentURL {
					// Navigation completed
					return nil
				}
			}
		}
	}

	// Wait for navigation but don't fail if it times out
	err = waitNav()
	if err != nil {
		// Log the error but don't fail
		fmt.Printf("Warning: navigation after form submission may not have completed: %v\n", err)
	}

	return nil
}

// clickElement clicks on an element
func (d *Daemon) clickElement(tabID, selector string, selectionTimeout, actionTimeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element with optional timeout
	var element *rod.Element
	if selectionTimeout > 0 {
		// Use timeout if specified
		element, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element (timeout after %ds): %w", selectionTimeout, err)
		}
	} else {
		// No timeout
		element, err = page.Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element: %w", err)
		}
	}

	// Make sure the element is visible and scrolled into view
	err = element.ScrollIntoView()
	if err != nil {
		return fmt.Errorf("failed to scroll element into view: %w", err)
	}

	// Click the element with optional timeout
	if actionTimeout > 0 {
		// Use timeout for the click action
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(actionTimeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the click in a goroutine
		go func() {
			err := element.Click(proto.InputMouseButtonLeft, 1) // 1 click
			done <- err
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to click element: %w", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("click action timed out after %d seconds", actionTimeout)
		}
	} else {
		// No timeout
		err = element.Click(proto.InputMouseButtonLeft, 1) // 1 click
		if err != nil {
			return fmt.Errorf("failed to click element: %w", err)
		}
	}

	// Wait a moment for any navigation to start
	time.Sleep(100 * time.Millisecond)

	// Wait for any potential page load or DOM changes
	err = page.WaitStable(1 * time.Second)
	if err != nil {
		// This is not a critical error, so we'll just log it
		log.Printf("Warning: page not stable after click: %v", err)
	}

	return nil
}

// selectElement selects an option in a select dropdown
func (d *Daemon) selectElement(tabID, selector, value string, selectionTimeout, actionTimeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element with optional timeout
	var element *rod.Element
	if selectionTimeout > 0 {
		// Use timeout if specified
		element, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element (timeout after %ds): %w", selectionTimeout, err)
		}
	} else {
		// No timeout
		element, err = page.Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element: %w", err)
		}
	}

	// Make sure the element is visible and scrolled into view
	err = element.ScrollIntoView()
	if err != nil {
		return fmt.Errorf("failed to scroll element into view: %w", err)
	}

	// For select elements, use rod's built-in Select method
	// Try to select by text first (most common case)
	err = element.Select([]string{value}, true, rod.SelectorTypeText)
	if err != nil {
		// If text selection failed, use JavaScript as fallback
		// Use a simple single statement that works with rod's evaluation
		script := fmt.Sprintf("document.querySelector(\"%s\").value = \"%s\"", selector, value)

		// Execute the value assignment (ignore evaluation errors, rod has issues with some JS patterns)
		page.Eval(script)

		// Dispatch the change event separately
		changeScript := fmt.Sprintf("document.querySelector(\"%s\").dispatchEvent(new Event(\"change\", { bubbles: true }))", selector)
		page.Eval(changeScript)

		// Verify the selection worked by checking the element's value property directly
		currentValue, err := element.Property("value")
		if err != nil {
			return fmt.Errorf("failed to verify selection: %w", err)
		}

		// Check if the selection actually worked
		if currentValue.Str() != value {
			return fmt.Errorf("failed to select option '%s' in element (current value: %s)", value, currentValue.Str())
		}
	}

	return nil
}

// evalJS executes JavaScript code in a tab and returns the result
func (d *Daemon) evalJS(tabID, jsCode string, timeout int) (string, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return "", err
	}

	// Create a comprehensive wrapper that handles both expressions and statements
	// and properly formats the result
	wrappedCode := `() => {
		var result;
		try {
			// Try to evaluate as an expression first
			result = eval(` + "`" + jsCode + "`" + `);
		} catch(e) {
			// If that fails, try to execute as statements
			try {
				eval(` + "`" + jsCode + "`" + `);
				result = undefined;
			} catch(e2) {
				throw e; // Re-throw the original error
			}
		}

		// Format the result for return
		if (typeof result === 'undefined') return 'undefined';
		if (result === null) return 'null';
		if (typeof result === 'string') return result;
		if (typeof result === 'number' || typeof result === 'boolean') return String(result);
		try {
			return JSON.stringify(result);
		} catch(e) {
			return String(result);
		}
	}`

	// Execute the wrapped JavaScript code with timeout
	if timeout > 0 {
		// Use timeout for the JavaScript execution
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan struct {
			result string
			err    error
		}, 1)

		// Execute the JavaScript in a goroutine
		go func() {
			result, err := page.Eval(wrappedCode)
			var resultStr string
			if err == nil && result != nil {
				// Convert the result to a string representation
				if result.Value.Nil() {
					resultStr = "null"
				} else {
					resultStr = result.Value.String()
				}
			}
			done <- struct {
				result string
				err    error
			}{resultStr, err}
		}()

		// Wait for either completion or timeout
		select {
		case res := <-done:
			if res.err != nil {
				return "", fmt.Errorf("failed to execute JavaScript: %w", res.err)
			}
			return res.result, nil
		case <-ctx.Done():
			return "", fmt.Errorf("JavaScript execution timed out after %d seconds", timeout)
		}
	} else {
		// No timeout
		result, err := page.Eval(wrappedCode)
		if err != nil {
			return "", fmt.Errorf("failed to execute JavaScript: %w", err)
		}
		if result == nil {
			return "", fmt.Errorf("JavaScript execution returned nil result")
		}

		// Convert the result to a string representation
		if result.Value.Nil() {
			return "null", nil
		}

		return result.Value.String(), nil
	}
}

// takeScreenshot takes a screenshot of a tab and saves it to a file
func (d *Daemon) takeScreenshot(tabID, outputPath string, fullPage bool, timeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	if timeout > 0 {
		// Use timeout for taking screenshot
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the screenshot in a goroutine
		go func() {
			// Take screenshot and save it
			screenshotBytes, err := page.Screenshot(fullPage, &proto.PageCaptureScreenshot{
				Format: proto.PageCaptureScreenshotFormatPng,
			})

			if err != nil {
				done <- fmt.Errorf("failed to capture screenshot: %w", err)
				return
			}

			// Write the screenshot to file
			err = os.WriteFile(outputPath, screenshotBytes, 0644)
			if err != nil {
				done <- fmt.Errorf("failed to save screenshot to %s: %w", outputPath, err)
				return
			}

			done <- nil
		}()

		// Wait for either completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to save screenshot: %w", err)
			}
			return nil
		case <-ctx.Done():
			return fmt.Errorf("taking screenshot timed out after %d seconds", timeout)
		}
	} else {
		// No timeout - take screenshot directly
		screenshotBytes, err := page.Screenshot(fullPage, &proto.PageCaptureScreenshot{
			Format: proto.PageCaptureScreenshotFormatPng,
		})

		if err != nil {
			return fmt.Errorf("failed to capture screenshot: %w", err)
		}

		// Write the screenshot to file
		err = os.WriteFile(outputPath, screenshotBytes, 0644)
		if err != nil {
			return fmt.Errorf("failed to save screenshot to %s: %w", outputPath, err)
		}

		return nil
	}
}

// takeScreenshotEnhanced takes a screenshot with optional zoom level and viewport size
func (d *Daemon) takeScreenshotEnhanced(tabID, outputPath string, fullPage bool, zoomLevel float64, viewportWidth, viewportHeight, timeout int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Store original viewport settings if we need to change them
	var originalViewport *proto.EmulationSetDeviceMetricsOverride
	needsReset := false

	// Get current viewport if we need to modify it
	if zoomLevel > 0 || viewportWidth > 0 || viewportHeight > 0 {
		currentViewport, err := page.Eval(`() => {
			return {
				width: window.innerWidth,
				height: window.innerHeight
			};
		}`)
		if err == nil && currentViewport != nil {
			var viewportData struct {
				Width  int `json:"width"`
				Height int `json:"height"`
			}
			json.Unmarshal([]byte(currentViewport.Value.String()), &viewportData)

			originalViewport = &proto.EmulationSetDeviceMetricsOverride{
				Width:             viewportData.Width,
				Height:            viewportData.Height,
				DeviceScaleFactor: 1.0,
				Mobile:            false,
			}
			needsReset = true

			// Set new viewport settings
			newWidth := viewportData.Width
			newHeight := viewportData.Height
			newZoom := 1.0

			if viewportWidth > 0 {
				newWidth = viewportWidth
			}
			if viewportHeight > 0 {
				newHeight = viewportHeight
			}
			if zoomLevel > 0 {
				newZoom = zoomLevel
			}

			err = page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{
				Width:             newWidth,
				Height:            newHeight,
				DeviceScaleFactor: newZoom,
				Mobile:            newWidth <= 768,
			})
			if err != nil {
				return fmt.Errorf("failed to set viewport: %w", err)
			}

			// Wait for reflow
			time.Sleep(500 * time.Millisecond)
		}
	}

	// Take the screenshot
	var screenshotErr error
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan error, 1)
		go func() {
			screenshotBytes, err := page.Screenshot(fullPage, &proto.PageCaptureScreenshot{
				Format: proto.PageCaptureScreenshotFormatPng,
			})
			if err != nil {
				done <- fmt.Errorf("failed to capture screenshot: %w", err)
				return
			}

			err = os.WriteFile(outputPath, screenshotBytes, 0644)
			if err != nil {
				done <- fmt.Errorf("failed to save screenshot to %s: %w", outputPath, err)
				return
			}
			done <- nil
		}()

		select {
		case err := <-done:
			screenshotErr = err
		case <-ctx.Done():
			screenshotErr = fmt.Errorf("taking screenshot timed out after %d seconds", timeout)
		}
	} else {
		screenshotBytes, err := page.Screenshot(fullPage, &proto.PageCaptureScreenshot{
			Format: proto.PageCaptureScreenshotFormatPng,
		})
		if err != nil {
			screenshotErr = fmt.Errorf("failed to capture screenshot: %w", err)
		} else {
			err = os.WriteFile(outputPath, screenshotBytes, 0644)
			if err != nil {
				screenshotErr = fmt.Errorf("failed to save screenshot to %s: %w", outputPath, err)
			}
		}
	}

	// Reset viewport if we changed it
	if needsReset && originalViewport != nil {
		err = page.SetViewport(originalViewport)
		if err != nil {
			d.debugLog("Warning: Failed to reset viewport: %v", err)
		}
	}

	return screenshotErr
}

// switchToIframe switches the context to an iframe for subsequent commands
func (d *Daemon) switchToIframe(tabID, selector string, timeout int) error {
	d.debugLog("Switching to iframe: selector=%s, tab=%s, timeout=%d", selector, tabID, timeout)

	// Get the main page first (not iframe context)
	actualTabID, err := d.getTabID(tabID)
	if err != nil {
		d.debugLog("Failed to get tab ID: %v", err)
		return err
	}

	d.mu.Lock()
	defer d.mu.Unlock()

	// Get the main page (bypass iframe context)
	mainPage, exists := d.tabs[actualTabID]
	if !exists {
		d.debugLog("Tab %s not in cache, trying to find it", actualTabID)
		// Try to find it
		mainPage, err = d.findPageByID(actualTabID)
		if err != nil {
			d.debugLog("Failed to find tab %s: %v", actualTabID, err)
			return err
		}
		if mainPage == nil {
			d.debugLog("Tab %s not found", actualTabID)
			return fmt.Errorf("tab not found: %s", actualTabID)
		}
		d.tabs[actualTabID] = mainPage
	}

	d.debugLog("Found main page for tab %s, looking for iframe element", actualTabID)

	// Find the iframe element with timeout
	var iframeElement *rod.Element
	if timeout > 0 {
		// Use timeout context for finding the iframe element
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan struct {
			element *rod.Element
			err     error
		}, 1)

		// Execute the element search in a goroutine
		go func() {
			defer func() {
				if r := recover(); r != nil {
					done <- struct {
						element *rod.Element
						err     error
					}{nil, fmt.Errorf("iframe element search panicked: %v", r)}
				}
			}()

			element, err := mainPage.Timeout(time.Duration(timeout) * time.Second).Element(selector)
			done <- struct {
				element *rod.Element
				err     error
			}{element, err}
		}()

		// Wait for either completion or timeout
		select {
		case result := <-done:
			iframeElement = result.element
			err = result.err
		case <-ctx.Done():
			d.debugLog("Iframe element search timed out after %d seconds", timeout)
			return fmt.Errorf("failed to find iframe element (timeout after %ds): %s", timeout, selector)
		}
	} else {
		// No timeout
		iframeElement, err = mainPage.Element(selector)
	}

	if err != nil {
		d.debugLog("Failed to find iframe element: %v", err)
		return fmt.Errorf("failed to find iframe element: %w", err)
	}

	d.debugLog("Found iframe element, getting frame context")

	// Get the iframe's page context with timeout
	var iframePage *rod.Page
	if timeout > 0 {
		// Use timeout context for getting the frame
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan struct {
			page *rod.Page
			err  error
		}, 1)

		// Execute the frame access in a goroutine
		go func() {
			defer func() {
				if r := recover(); r != nil {
					done <- struct {
						page *rod.Page
						err  error
					}{nil, fmt.Errorf("iframe frame access panicked: %v", r)}
				}
			}()

			page, err := iframeElement.Frame()
			done <- struct {
				page *rod.Page
				err  error
			}{page, err}
		}()

		// Wait for either completion or timeout
		select {
		case result := <-done:
			iframePage = result.page
			err = result.err
		case <-ctx.Done():
			d.debugLog("Iframe frame access timed out after %d seconds", timeout)
			return fmt.Errorf("failed to get iframe context (timeout after %ds)", timeout)
		}
	} else {
		// No timeout
		iframePage, err = iframeElement.Frame()
	}

	if err != nil {
		d.debugLog("Failed to get iframe context: %v", err)
		return fmt.Errorf("failed to get iframe context: %w", err)
	}

	// Store the iframe page context
	d.iframePages[actualTabID] = iframePage
	d.debugLog("Successfully switched to iframe context for tab %s", actualTabID)

	return nil
}

// switchToMain switches back to the main page context
func (d *Daemon) switchToMain(tabID string) error {
	d.debugLog("Switching back to main context: tab=%s", tabID)

	// Get the tab ID to use (may be the current tab)
	actualTabID, err := d.getTabID(tabID)
	if err != nil {
		d.debugLog("Failed to get tab ID: %v", err)
		return err
	}

	d.mu.Lock()
	defer d.mu.Unlock()

	// Check if there was an iframe context to remove
	if _, exists := d.iframePages[actualTabID]; exists {
		d.debugLog("Removing iframe context for tab %s", actualTabID)
		// Remove the iframe context for this tab
		delete(d.iframePages, actualTabID)
	} else {
		d.debugLog("No iframe context found for tab %s", actualTabID)
	}

	d.debugLog("Successfully switched back to main context for tab %s", actualTabID)
	return nil
}

// handleFileUpload handles file upload requests from clients
func (d *Daemon) handleFileUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Parse multipart form (32MB max memory)
	err := r.ParseMultipartForm(32 << 20)
	if err != nil {
		http.Error(w, "Failed to parse multipart form", http.StatusBadRequest)
		return
	}

	// Get the uploaded file
	file, header, err := r.FormFile("file")
	if err != nil {
		http.Error(w, "Failed to get uploaded file", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// Get the target path (optional, defaults to /tmp/)
	targetPath := r.FormValue("path")
	if targetPath == "" {
		targetPath = "/tmp/" + header.Filename
	}

	// Create the target file
	targetFile, err := os.Create(targetPath)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to create target file: %v", err), http.StatusInternalServerError)
		return
	}
	defer targetFile.Close()

	// Copy the uploaded file to the target location
	_, err = io.Copy(targetFile, file)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to save file: %v", err), http.StatusInternalServerError)
		return
	}

	// Return success response
	response := Response{
		Success: true,
		Data: map[string]interface{}{
			"filename":    header.Filename,
			"size":        header.Size,
			"target_path": targetPath,
		},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// handleFileDownload handles file download requests from clients
func (d *Daemon) handleFileDownload(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Get the file path from query parameter
	filePath := r.URL.Query().Get("path")
	if filePath == "" {
		http.Error(w, "File path is required", http.StatusBadRequest)
		return
	}

	// Check if file exists and get info
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, "File not found", http.StatusNotFound)
		} else {
			http.Error(w, fmt.Sprintf("Failed to access file: %v", err), http.StatusInternalServerError)
		}
		return
	}

	// Open the file
	file, err := os.Open(filePath)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to open file: %v", err), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	// Set headers for file download
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileInfo.Name()))
	w.Header().Set("Content-Length", strconv.FormatInt(fileInfo.Size(), 10))

	// Stream the file to the client
	_, err = io.Copy(w, file)
	if err != nil {
		log.Printf("Error streaming file to client: %v", err)
	}
}

// setupConsoleLogging sets up console log capture for a tab
func (d *Daemon) setupConsoleLogging(tabID string, page *rod.Page) {
	// Initialize console logs for this tab
	d.consoleLogs[tabID] = make([]ConsoleLog, 0)

	// Listen for console events
	go page.EachEvent(func(e *proto.RuntimeConsoleAPICalled) {
		d.mu.Lock()
		defer d.mu.Unlock()

		// Convert console level
		level := string(e.Type)

		// Build message from arguments
		var message string
		for i, arg := range e.Args {
			if i > 0 {
				message += " "
			}
			// Handle different argument types
			if !arg.Value.Nil() {
				message += arg.Value.String()
			} else if arg.Description != "" {
				message += arg.Description
			} else {
				message += "[object]"
			}
		}

		// Create console log entry
		logEntry := ConsoleLog{
			Level:     level,
			Message:   message,
			Timestamp: time.Now(),
			Source:    "", // Could be enhanced with stack trace info
		}

		// Add to console logs (keep last 1000 entries per tab)
		logs := d.consoleLogs[tabID]
		logs = append(logs, logEntry)
		if len(logs) > 1000 {
			logs = logs[1:] // Remove oldest entry
		}
		d.consoleLogs[tabID] = logs
	})()
}

// getConsoleLogs retrieves console logs for a tab
func (d *Daemon) getConsoleLogs(tabID string, clear bool) ([]ConsoleLog, error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	// Check if tab exists
	if _, exists := d.tabs[tabID]; !exists {
		return nil, fmt.Errorf("tab not found: %s", tabID)
	}

	// Get logs for this tab
	logs, exists := d.consoleLogs[tabID]
	if !exists {
		logs = make([]ConsoleLog, 0)
	}

	// Clear logs if requested
	if clear {
		d.consoleLogs[tabID] = make([]ConsoleLog, 0)
	}

	return logs, nil
}

// executeConsoleCommand executes a command in the browser console
func (d *Daemon) executeConsoleCommand(tabID, command string, timeout int) (string, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return "", fmt.Errorf("no tab specified and no current tab available")
	}

	// Execute the command as JavaScript and return the result
	// This is similar to evalJS but specifically for console commands
	return d.evalJS(tabID, command, timeout)
}

// ElementCheckResult represents the result of an element check
type ElementCheckResult struct {
	Exists   bool `json:"exists"`
	Visible  bool `json:"visible,omitempty"`
	Enabled  bool `json:"enabled,omitempty"`
	Focused  bool `json:"focused,omitempty"`
	Selected bool `json:"selected,omitempty"`
	Count    int  `json:"count,omitempty"`
}

// MultipleExtractionResult represents the result of extracting from multiple selectors
type MultipleExtractionResult struct {
	Results map[string]interface{} `json:"results"`
	Errors  map[string]string      `json:"errors,omitempty"`
}

// LinkInfo represents information about a link
type LinkInfo struct {
	Href   string `json:"href"`
	Text   string `json:"text"`
	Title  string `json:"title,omitempty"`
	Target string `json:"target,omitempty"`
}

// LinksExtractionResult represents the result of extracting links
type LinksExtractionResult struct {
	Links []LinkInfo `json:"links"`
	Count int        `json:"count"`
}

// TableExtractionResult represents the result of extracting table data
type TableExtractionResult struct {
	Headers []string            `json:"headers,omitempty"`
	Rows    [][]string          `json:"rows"`
	Data    []map[string]string `json:"data,omitempty"` // Only if headers are included
	Count   int                 `json:"count"`
}

// TextExtractionResult represents the result of extracting text
type TextExtractionResult struct {
	Text    string   `json:"text"`
	Matches []string `json:"matches,omitempty"` // If pattern was used
	Count   int      `json:"count"`             // Number of elements matched
}

// checkElement checks various states of an element
func (d *Daemon) checkElement(tabID, selector, checkType string, timeout int) (*ElementCheckResult, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return nil, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	result := &ElementCheckResult{}

	// First check if element exists
	elements, err := page.Elements(selector)
	if err != nil {
		// If we can't find elements, it means they don't exist
		result.Exists = false
		return result, nil
	}

	result.Exists = len(elements) > 0
	result.Count = len(elements)

	// If no elements exist, return early
	if !result.Exists {
		return result, nil
	}

	// For additional checks, use the first element
	element := elements[0]

	switch checkType {
	case "exists":
		// Already handled above
	case "visible":
		visible, err := element.Visible()
		if err != nil {
			return nil, fmt.Errorf("failed to check visibility: %w", err)
		}
		result.Visible = visible
	case "enabled":
		// Check if element is enabled (not disabled)
		disabled, err := element.Attribute("disabled")
		if err != nil {
			return nil, fmt.Errorf("failed to check enabled state: %w", err)
		}
		result.Enabled = disabled == nil
	case "focused":
		// Check if element is focused
		jsCode := fmt.Sprintf("document.activeElement === document.querySelector('%s')", selector)
		focusResult, err := page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to check focus state: %w", err)
		}
		result.Focused = focusResult.Value.Bool()
	case "selected":
		// Check if element is selected (for checkboxes, radio buttons, options)
		selected, err := element.Attribute("selected")
		if err == nil && selected != nil {
			result.Selected = true
		} else {
			// Also check 'checked' attribute for checkboxes and radio buttons
			checked, err := element.Attribute("checked")
			if err == nil && checked != nil {
				result.Selected = true
			} else {
				result.Selected = false
			}
		}
	case "all":
		// Check all states
		visible, _ := element.Visible()
		result.Visible = visible

		disabled, _ := element.Attribute("disabled")
		result.Enabled = disabled == nil

		jsCode := fmt.Sprintf("document.activeElement === document.querySelector('%s')", selector)
		focusResult, _ := page.Eval(jsCode)
		if focusResult != nil {
			result.Focused = focusResult.Value.Bool()
		}

		selected, _ := element.Attribute("selected")
		if selected != nil {
			result.Selected = true
		} else {
			checked, _ := element.Attribute("checked")
			result.Selected = checked != nil
		}
	default:
		return nil, fmt.Errorf("unknown check type: %s", checkType)
	}

	return result, nil
}

// getElementAttributes gets attributes, properties, and computed styles of an element
func (d *Daemon) getElementAttributes(tabID, selector, attributes string, timeout int) (map[string]interface{}, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return nil, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	// Find the element with timeout
	var element *rod.Element
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		var err error
		element, err = page.Context(ctx).Element(selector)
		if err != nil {
			return nil, fmt.Errorf("element not found: %w", err)
		}
	} else {
		var err error
		element, err = page.Element(selector)
		if err != nil {
			return nil, fmt.Errorf("element not found: %w", err)
		}
	}

	result := make(map[string]interface{})

	if attributes == "all" {
		// Get all common attributes and properties
		commonAttrs := []string{
			"id", "class", "name", "type", "value", "href", "src", "alt", "title",
			"disabled", "checked", "selected", "readonly", "required", "placeholder",
			"data-*", "aria-*",
		}

		// Get HTML attributes
		for _, attr := range commonAttrs {
			if attr == "data-*" || attr == "aria-*" {
				// Skip wildcard attributes for now
				continue
			}
			value, err := element.Attribute(attr)
			if err == nil && value != nil {
				result[attr] = *value
			}
		}

		// Get common properties via JavaScript
		jsCode := `
			(function(el) {
				return {
					tagName: el.tagName,
					textContent: el.textContent,
					innerHTML: el.innerHTML,
					outerHTML: el.outerHTML,
					offsetWidth: el.offsetWidth,
					offsetHeight: el.offsetHeight,
					scrollWidth: el.scrollWidth,
					scrollHeight: el.scrollHeight,
					clientWidth: el.clientWidth,
					clientHeight: el.clientHeight
				};
			})(arguments[0])
		`

		jsResult, err := element.Eval(jsCode)
		if err == nil {
			if props := jsResult.Value.Map(); props != nil {
				for key, value := range props {
					result[key] = value
				}
			}
		}

		// Get computed styles for common properties
		styleProps := []string{
			"display", "visibility", "opacity", "position", "top", "left", "width", "height",
			"margin", "padding", "border", "background-color", "color", "font-size", "font-family",
		}

		for _, prop := range styleProps {
			jsCode := fmt.Sprintf("getComputedStyle(arguments[0]).%s", prop)
			styleResult, err := element.Eval(jsCode)
			if err == nil {
				result["style_"+prop] = styleResult.Value.Str()
			}
		}
	} else {
		// Get specific attributes (comma-separated)
		attrList := []string{}
		if attributes != "" {
			// Split by comma and trim spaces
			for _, attr := range strings.Split(attributes, ",") {
				attrList = append(attrList, strings.TrimSpace(attr))
			}
		}

		for _, attr := range attrList {
			if strings.HasPrefix(attr, "style_") {
				// Get computed style
				styleProp := strings.TrimPrefix(attr, "style_")
				jsCode := fmt.Sprintf("getComputedStyle(arguments[0]).%s", styleProp)
				styleResult, err := element.Eval(jsCode)
				if err == nil {
					result[attr] = styleResult.Value.Str()
				}
			} else if strings.HasPrefix(attr, "prop_") {
				// Get JavaScript property
				propName := strings.TrimPrefix(attr, "prop_")
				jsCode := fmt.Sprintf("arguments[0].%s", propName)
				propResult, err := element.Eval(jsCode)
				if err == nil {
					result[attr] = propResult.Value.Raw
				}
			} else {
				// Get HTML attribute
				value, err := element.Attribute(attr)
				if err == nil && value != nil {
					result[attr] = *value
				}
			}
		}
	}

	return result, nil
}

// countElements counts the number of elements matching a selector
func (d *Daemon) countElements(tabID, selector string, timeout int) (int, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return 0, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return 0, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	// Find elements with timeout
	var elements rod.Elements
	var err error

	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		elements, err = page.Context(ctx).Elements(selector)
	} else {
		elements, err = page.Elements(selector)
	}

	if err != nil {
		// If we can't find elements, return 0 (not an error)
		return 0, nil
	}

	return len(elements), nil
}

// extractMultiple extracts data from multiple selectors in a single call
func (d *Daemon) extractMultiple(tabID, selectorsJSON string, timeout int) (*MultipleExtractionResult, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return nil, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	// Parse selectors JSON
	var selectors map[string]string
	if err := json.Unmarshal([]byte(selectorsJSON), &selectors); err != nil {
		return nil, fmt.Errorf("invalid selectors JSON: %w", err)
	}

	result := &MultipleExtractionResult{
		Results: make(map[string]interface{}),
		Errors:  make(map[string]string),
	}

	// Extract from each selector
	for key, selector := range selectors {
		var elements rod.Elements
		var err error

		if timeout > 0 {
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
			defer cancel()
			elements, err = page.Context(ctx).Elements(selector)
		} else {
			elements, err = page.Elements(selector)
		}

		if err != nil {
			result.Errors[key] = err.Error()
			continue
		}

		if len(elements) == 0 {
			result.Results[key] = nil
			continue
		}

		// Extract content from all matching elements
		var values []string
		for _, element := range elements {
			var value string
			var err error

			// Check if it's a form input element and get its value
			tagName, err := element.Eval("() => this.tagName.toLowerCase()")

			if err == nil && tagName != nil && (tagName.Value.Str() == "input" || tagName.Value.Str() == "textarea" || tagName.Value.Str() == "select") {
				// For form elements, get the value property
				valueProp, err := element.Property("value")
				if err == nil && valueProp.Str() != "" {
					value = valueProp.Str()
				} else {
					// Fallback to text content
					value, err = element.Text()
				}
			} else {
				// For non-form elements, get text content
				value, err = element.Text()
			}

			if err != nil {
				result.Errors[key] = fmt.Sprintf("failed to get content: %v", err)
				break
			}
			values = append(values, value)
		}

		if len(values) == 1 {
			result.Results[key] = values[0]
		} else {
			result.Results[key] = values
		}
	}

	return result, nil
}

// extractLinks extracts all links from the page with optional filtering
func (d *Daemon) extractLinks(tabID, containerSelector, hrefPattern, textPattern string, timeout int) (*LinksExtractionResult, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return nil, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	// Build selector for links
	linkSelector := "a[href]"
	if containerSelector != "" {
		linkSelector = containerSelector + " " + linkSelector
	}

	// Find all links
	var elements rod.Elements
	var err error

	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()
		elements, err = page.Context(ctx).Elements(linkSelector)
	} else {
		elements, err = page.Elements(linkSelector)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to find links: %w", err)
	}

	result := &LinksExtractionResult{
		Links: make([]LinkInfo, 0),
		Count: 0,
	}

	// Compile regex patterns if provided
	var hrefRegex, textRegex *regexp.Regexp
	if hrefPattern != "" {
		hrefRegex, err = regexp.Compile(hrefPattern)
		if err != nil {
			return nil, fmt.Errorf("invalid href pattern: %w", err)
		}
	}
	if textPattern != "" {
		textRegex, err = regexp.Compile(textPattern)
		if err != nil {
			return nil, fmt.Errorf("invalid text pattern: %w", err)
		}
	}

	// Extract link information
	for _, element := range elements {
		href, err := element.Attribute("href")
		if err != nil || href == nil {
			continue
		}

		text, err := element.Text()
		if err != nil {
			text = ""
		}

		// Apply filters
		if hrefRegex != nil && !hrefRegex.MatchString(*href) {
			continue
		}
		if textRegex != nil && !textRegex.MatchString(text) {
			continue
		}

		// Get additional attributes
		title, _ := element.Attribute("title")
		target, _ := element.Attribute("target")

		linkInfo := LinkInfo{
			Href: *href,
			Text: text,
		}
		if title != nil {
			linkInfo.Title = *title
		}
		if target != nil {
			linkInfo.Target = *target
		}

		result.Links = append(result.Links, linkInfo)
	}

	result.Count = len(result.Links)
	return result, nil
}

// extractTable extracts table data as structured JSON
func (d *Daemon) extractTable(tabID, selector string, includeHeaders bool, timeout int) (*TableExtractionResult, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return nil, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	// Find the table
	var table *rod.Element
	var err error

	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()
		table, err = page.Context(ctx).Element(selector)
	} else {
		table, err = page.Element(selector)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to find table: %w", err)
	}

	result := &TableExtractionResult{
		Rows:  make([][]string, 0),
		Count: 0,
	}

	// Extract headers if requested
	if includeHeaders {
		headerRows, err := table.Elements("thead tr, tr:first-child")
		if err == nil && len(headerRows) > 0 {
			headerCells, err := headerRows[0].Elements("th, td")
			if err == nil {
				headers := make([]string, 0)
				for _, cell := range headerCells {
					text, err := cell.Text()
					if err != nil {
						text = ""
					}
					headers = append(headers, strings.TrimSpace(text))
				}
				result.Headers = headers
			}
		}
	}

	// Extract all rows
	rows, err := table.Elements("tbody tr, tr")
	if err != nil {
		return nil, fmt.Errorf("failed to find table rows: %w", err)
	}

	// Skip header row if we extracted headers
	startIndex := 0
	if includeHeaders && len(result.Headers) > 0 {
		startIndex = 1
	}

	for i := startIndex; i < len(rows); i++ {
		cells, err := rows[i].Elements("td, th")
		if err != nil {
			continue
		}

		rowData := make([]string, 0)
		for _, cell := range cells {
			text, err := cell.Text()
			if err != nil {
				text = ""
			}
			rowData = append(rowData, strings.TrimSpace(text))
		}

		if len(rowData) > 0 {
			result.Rows = append(result.Rows, rowData)
		}
	}

	// Create structured data if headers are available
	if includeHeaders && len(result.Headers) > 0 {
		result.Data = make([]map[string]string, 0)
		for _, row := range result.Rows {
			rowMap := make(map[string]string)
			for i, header := range result.Headers {
				if i < len(row) {
					rowMap[header] = row[i]
				} else {
					rowMap[header] = ""
				}
			}
			result.Data = append(result.Data, rowMap)
		}
	}

	result.Count = len(result.Rows)
	return result, nil
}

// extractText extracts text content with optional pattern matching
func (d *Daemon) extractText(tabID, selector, pattern, extractType string, timeout int) (*TextExtractionResult, error) {
	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, exists := d.tabs[tabID]
	if !exists {
		return nil, fmt.Errorf("tab %s not found", tabID)
	}

	// Check if we're in iframe mode for this tab
	if iframePage, inIframe := d.iframePages[tabID]; inIframe {
		page = iframePage
	}

	// Default extract type
	if extractType == "" {
		extractType = "textContent"
	}

	// Find elements
	var elements rod.Elements
	var err error

	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()
		elements, err = page.Context(ctx).Elements(selector)
	} else {
		elements, err = page.Elements(selector)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to find elements: %w", err)
	}

	result := &TextExtractionResult{
		Count: len(elements),
	}

	// Compile regex pattern if provided
	var textRegex *regexp.Regexp
	if pattern != "" {
		textRegex, err = regexp.Compile(pattern)
		if err != nil {
			return nil, fmt.Errorf("invalid text pattern: %w", err)
		}
	}

	// Extract text from all elements
	var allTexts []string
	for _, element := range elements {
		var text string

		switch extractType {
		case "text":
			text, err = element.Text()
		case "innerText":
			// Use JavaScript to get innerText
			jsResult, jsErr := element.Eval("() => this.innerText")
			if jsErr == nil && jsResult != nil && jsResult.Value.Str() != "" {
				text = jsResult.Value.Str()
			} else {
				text, err = element.Text() // Fallback
			}
		case "textContent":
			// Use JavaScript to get textContent
			jsResult, jsErr := element.Eval("() => this.textContent")
			if jsErr == nil && jsResult != nil && jsResult.Value.Str() != "" {
				text = jsResult.Value.Str()
			} else {
				text, err = element.Text() // Fallback
			}
		default:
			text, err = element.Text()
		}

		if err != nil {
			continue
		}

		allTexts = append(allTexts, text)
	}

	// Join all texts
	result.Text = strings.Join(allTexts, "\n")

	// Apply pattern matching if provided
	if textRegex != nil {
		matches := textRegex.FindAllString(result.Text, -1)
		result.Matches = matches
	}

	return result, nil
}

// FormField represents a form field with its properties
type FormField struct {
	Name        string            `json:"name"`
	Type        string            `json:"type"`
	Value       string            `json:"value"`
	Placeholder string            `json:"placeholder,omitempty"`
	Required    bool              `json:"required"`
	Disabled    bool              `json:"disabled"`
	ReadOnly    bool              `json:"readonly"`
	Selector    string            `json:"selector"`
	Label       string            `json:"label,omitempty"`
	Options     []FormFieldOption `json:"options,omitempty"` // For select/radio/checkbox
}

// FormFieldOption represents an option in a select, radio, or checkbox group
type FormFieldOption struct {
	Value    string `json:"value"`
	Text     string `json:"text"`
	Selected bool   `json:"selected"`
}

// FormAnalysisResult represents the result of analyzing a form
type FormAnalysisResult struct {
	Action     string      `json:"action,omitempty"`
	Method     string      `json:"method,omitempty"`
	Fields     []FormField `json:"fields"`
	FieldCount int         `json:"field_count"`
	CanSubmit  bool        `json:"can_submit"`
	SubmitText string      `json:"submit_text,omitempty"`
}

// InteractionItem represents a single interaction to perform
type InteractionItem struct {
	Selector string `json:"selector"`
	Action   string `json:"action"` // click, fill, select, check, uncheck
	Value    string `json:"value,omitempty"`
}

// InteractionResult represents the result of a single interaction
type InteractionResult struct {
	Selector string `json:"selector"`
	Action   string `json:"action"`
	Success  bool   `json:"success"`
	Error    string `json:"error,omitempty"`
}

// MultipleInteractionResult represents the result of multiple interactions
type MultipleInteractionResult struct {
	Results      []InteractionResult `json:"results"`
	SuccessCount int                 `json:"success_count"`
	ErrorCount   int                 `json:"error_count"`
	TotalCount   int                 `json:"total_count"`
}

// FormBulkFillResult represents the result of bulk form filling
type FormBulkFillResult struct {
	FilledFields []InteractionResult `json:"filled_fields"`
	SuccessCount int                 `json:"success_count"`
	ErrorCount   int                 `json:"error_count"`
	TotalCount   int                 `json:"total_count"`
}

// analyzeForm analyzes a form and returns detailed information about its fields
func (d *Daemon) analyzeForm(tabID, selector string, timeout int) (*FormAnalysisResult, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// Find the form element
	var form *rod.Element
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()
		form, err = page.Context(ctx).Element(selector)
	} else {
		form, err = page.Element(selector)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to find form: %w", err)
	}

	result := &FormAnalysisResult{
		Fields: make([]FormField, 0),
	}

	// Get form action and method
	if action, err := form.Attribute("action"); err == nil && action != nil {
		result.Action = *action
	}
	if method, err := form.Attribute("method"); err == nil && method != nil {
		result.Method = *method
	} else {
		result.Method = "GET" // Default
	}

	// Find all form fields
	fieldSelectors := []string{
		"input", "textarea", "select", "button[type='submit']", "input[type='submit']",
	}

	for _, fieldSelector := range fieldSelectors {
		elements, err := form.Elements(fieldSelector)
		if err != nil {
			continue
		}

		for _, element := range elements {
			field := FormField{}

			// Get basic attributes
			if name, err := element.Attribute("name"); err == nil && name != nil {
				field.Name = *name
			}
			if id, err := element.Attribute("id"); err == nil && id != nil && field.Name == "" {
				field.Name = *id
			}

			if fieldType, err := element.Attribute("type"); err == nil && fieldType != nil {
				field.Type = *fieldType
			} else {
				// Get tag name if no type
				if tagName, err := element.Eval("() => this.tagName.toLowerCase()"); err == nil && tagName != nil {
					field.Type = tagName.Value.Str()
				}
			}

			// Skip submit buttons for field analysis but note them for submission info
			if field.Type == "submit" {
				result.CanSubmit = true
				if value, err := element.Attribute("value"); err == nil && value != nil {
					result.SubmitText = *value
				} else if text, err := element.Text(); err == nil {
					result.SubmitText = text
				}
				continue
			}

			// Get current value
			if value, err := element.Attribute("value"); err == nil && value != nil {
				field.Value = *value
			}

			// Get placeholder
			if placeholder, err := element.Attribute("placeholder"); err == nil && placeholder != nil {
				field.Placeholder = *placeholder
			}

			// Get boolean attributes
			if required, err := element.Attribute("required"); err == nil && required != nil {
				field.Required = true
			}
			if disabled, err := element.Attribute("disabled"); err == nil && disabled != nil {
				field.Disabled = true
			}
			if readonly, err := element.Attribute("readonly"); err == nil && readonly != nil {
				field.ReadOnly = true
			}

			// Generate selector for this field
			if field.Name != "" {
				field.Selector = fmt.Sprintf("[name='%s']", field.Name)
			} else if id, err := element.Attribute("id"); err == nil && id != nil {
				field.Selector = fmt.Sprintf("#%s", *id)
			}

			// Try to find associated label
			if field.Name != "" {
				if label, err := form.Element(fmt.Sprintf("label[for='%s']", field.Name)); err == nil {
					if labelText, err := label.Text(); err == nil {
						field.Label = labelText
					}
				}
			}

			// Handle select options
			if field.Type == "select" {
				options, err := element.Elements("option")
				if err == nil {
					field.Options = make([]FormFieldOption, 0)
					for _, option := range options {
						opt := FormFieldOption{}
						if value, err := option.Attribute("value"); err == nil && value != nil {
							opt.Value = *value
						}
						if text, err := option.Text(); err == nil {
							opt.Text = text
						}
						if selected, err := option.Attribute("selected"); err == nil && selected != nil {
							opt.Selected = true
						}
						field.Options = append(field.Options, opt)
					}
				}
			}

			result.Fields = append(result.Fields, field)
		}
	}

	result.FieldCount = len(result.Fields)

	// Check if form can be submitted (has submit button or can be submitted via JS)
	if !result.CanSubmit {
		// Look for any button that might submit
		if buttons, err := form.Elements("button"); err == nil {
			for _, button := range buttons {
				if buttonType, err := button.Attribute("type"); err == nil && buttonType != nil {
					if *buttonType == "submit" || *buttonType == "" {
						result.CanSubmit = true
						if text, err := button.Text(); err == nil {
							result.SubmitText = text
						}
						break
					}
				}
			}
		}
	}

	return result, nil
}

// interactMultiple performs multiple interactions in sequence
func (d *Daemon) interactMultiple(tabID, interactionsJSON string, timeout int) (*MultipleInteractionResult, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// Parse interactions JSON
	var interactions []InteractionItem
	err = json.Unmarshal([]byte(interactionsJSON), &interactions)
	if err != nil {
		return nil, fmt.Errorf("failed to parse interactions JSON: %w", err)
	}

	result := &MultipleInteractionResult{
		Results:    make([]InteractionResult, 0),
		TotalCount: len(interactions),
	}

	// Perform each interaction
	for _, interaction := range interactions {
		interactionResult := InteractionResult{
			Selector: interaction.Selector,
			Action:   interaction.Action,
			Success:  false,
		}

		// Find the element without timeout to avoid context cancellation issues
		var element *rod.Element
		element, err = page.Element(interaction.Selector)

		if err != nil {
			interactionResult.Error = fmt.Sprintf("failed to find element: %v", err)
			result.Results = append(result.Results, interactionResult)
			result.ErrorCount++
			continue
		}

		// Perform the action
		switch interaction.Action {
		case "click":
			err = element.Click(proto.InputMouseButtonLeft, 1)
			// Retry once if context was canceled
			if err != nil && strings.Contains(err.Error(), "context canceled") {
				// Try to find element again and click
				element, err = page.Element(interaction.Selector)
				if err == nil {
					err = element.Click(proto.InputMouseButtonLeft, 1)
				}
			}
			if err != nil {
				interactionResult.Error = fmt.Sprintf("failed to click: %v", err)
			} else {
				interactionResult.Success = true
			}

		case "fill":
			// Clear field first
			err = element.SelectAllText()
			if err == nil {
				err = element.Input("")
			}
			if err == nil {
				err = element.Input(interaction.Value)
			}
			// Retry once if context was canceled
			if err != nil && strings.Contains(err.Error(), "context canceled") {
				// Try to find element again and fill
				element, err = page.Element(interaction.Selector)
				if err == nil {
					err = element.SelectAllText()
					if err == nil {
						err = element.Input("")
					}
					if err == nil {
						err = element.Input(interaction.Value)
					}
				}
			}
			if err != nil {
				interactionResult.Error = fmt.Sprintf("failed to fill: %v", err)
			} else {
				interactionResult.Success = true
			}

		case "select":
			// For select elements, use rod's built-in Select method
			// Try to select by text first (most common case)
			err = element.Select([]string{interaction.Value}, true, rod.SelectorTypeText)
			if err != nil {
				// If text selection failed, use JavaScript as fallback
				// Execute the value assignment (ignore evaluation errors, rod has issues with some JS patterns)
				script := fmt.Sprintf("document.querySelector(\"%s\").value = \"%s\"", interaction.Selector, interaction.Value)
				page.Eval(script)

				// Dispatch the change event separately
				changeScript := fmt.Sprintf("document.querySelector(\"%s\").dispatchEvent(new Event(\"change\", { bubbles: true }))", interaction.Selector)
				page.Eval(changeScript)

				// Verify the selection worked by checking the element's value property directly
				currentValue, err := element.Property("value")
				if err != nil {
					interactionResult.Error = fmt.Sprintf("failed to verify selection: %v", err)
				} else if currentValue.Str() != interaction.Value {
					interactionResult.Error = fmt.Sprintf("failed to select option '%s' (current value: %s)", interaction.Value, currentValue.Str())
				} else {
					interactionResult.Success = true
				}
			} else {
				interactionResult.Success = true
			}

		case "check":
			// Check if it's already checked
			checked, err := element.Property("checked")
			if err == nil && checked.Bool() {
				interactionResult.Success = true // Already checked
			} else {
				err = element.Click(proto.InputMouseButtonLeft, 1)
				// Retry once if context was canceled
				if err != nil && strings.Contains(err.Error(), "context canceled") {
					// Try to find element again and click
					element, err = page.Element(interaction.Selector)
					if err == nil {
						err = element.Click(proto.InputMouseButtonLeft, 1)
					}
				}
				if err != nil {
					interactionResult.Error = fmt.Sprintf("failed to check: %v", err)
				} else {
					interactionResult.Success = true
				}
			}

		case "uncheck":
			// Check if it's already unchecked
			checked, err := element.Property("checked")
			if err == nil && !checked.Bool() {
				interactionResult.Success = true // Already unchecked
			} else {
				err = element.Click(proto.InputMouseButtonLeft, 1)
				if err != nil {
					interactionResult.Error = fmt.Sprintf("failed to uncheck: %v", err)
				} else {
					interactionResult.Success = true
				}
			}

		default:
			interactionResult.Error = fmt.Sprintf("unknown action: %s", interaction.Action)
		}

		result.Results = append(result.Results, interactionResult)
		if interactionResult.Success {
			result.SuccessCount++
		} else {
			result.ErrorCount++
		}
	}

	return result, nil
}

// fillFormBulk fills multiple form fields in a single operation
func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout int) (*FormBulkFillResult, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// Parse fields JSON
	var fields map[string]string
	err = json.Unmarshal([]byte(fieldsJSON), &fields)
	if err != nil {
		return nil, fmt.Errorf("failed to parse fields JSON: %w", err)
	}

	result := &FormBulkFillResult{
		FilledFields: make([]InteractionResult, 0),
		TotalCount:   len(fields),
	}

	// Find the form element if selector is provided
	var form *rod.Element
	if formSelector != "" {
		if timeout > 0 {
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
			form, err = page.Context(ctx).Element(formSelector)
			cancel()
		} else {
			form, err = page.Element(formSelector)
		}

		if err != nil {
			return nil, fmt.Errorf("failed to find form: %w", err)
		}
	}

	// Fill each field
	for fieldName, fieldValue := range fields {
		fieldResult := InteractionResult{
			Selector: fieldName,
			Action:   "fill", // Default action, will be updated based on element type
			Success:  false,
		}

		// Try different selector strategies for the field (fast, no individual timeouts)
		var element *rod.Element
		selectors := []string{
			fmt.Sprintf("[name='%s']", fieldName),
			fmt.Sprintf("#%s", fieldName),
			fmt.Sprintf("[id='%s']", fieldName),
			fieldName, // In case it's already a full selector
		}

		// Search for element (try form first if available, then page)
		for _, selector := range selectors {
			// Try without timeout first (should be instant if element exists)
			if form != nil {
				element, err = form.Element(selector)
			} else {
				element, err = page.Element(selector)
			}

			if err == nil {
				fieldResult.Selector = selector
				break
			}
		}

		if element == nil {
			fieldResult.Error = fmt.Sprintf("failed to find field: %s", fieldName)
			result.FilledFields = append(result.FilledFields, fieldResult)
			result.ErrorCount++
			continue
		}

		// Determine the element type using rod's built-in method (much faster than Eval)
		tagName, err := element.Property("tagName")
		if err != nil {
			fieldResult.Error = fmt.Sprintf("failed to get element tag name: %v", err)
			result.FilledFields = append(result.FilledFields, fieldResult)
			result.ErrorCount++
			continue
		}

		// Handle different element types
		if strings.ToLower(tagName.Str()) == "select" {
			// Use select action for select elements
			fieldResult.Action = "select"
			err = element.Select([]string{fieldValue}, true, rod.SelectorTypeText)
			if err != nil {
				// If text selection failed, use JavaScript as fallback
				// Execute the value assignment (ignore evaluation errors, rod has issues with some JS patterns)
				script := fmt.Sprintf("document.querySelector(\"%s\").value = \"%s\"", fieldResult.Selector, fieldValue)
				page.Eval(script)

				// Dispatch the change event separately
				changeScript := fmt.Sprintf("document.querySelector(\"%s\").dispatchEvent(new Event(\"change\", { bubbles: true }))", fieldResult.Selector)
				page.Eval(changeScript)

				// Verify the selection worked by checking the element's value property directly
				currentValue, err := element.Property("value")
				if err != nil {
					fieldResult.Error = fmt.Sprintf("failed to verify selection: %v", err)
					result.ErrorCount++
				} else if currentValue.Str() != fieldValue {
					fieldResult.Error = fmt.Sprintf("failed to select option '%s' (current value: %s)", fieldValue, currentValue.Str())
					result.ErrorCount++
				} else {
					fieldResult.Success = true
					result.SuccessCount++
				}
			} else {
				fieldResult.Success = true
				result.SuccessCount++
			}
		} else {
			// Use fill action for input, textarea, etc.
			fieldResult.Action = "fill"
			err = element.SelectAllText()
			if err == nil {
				err = element.Input("")
			}
			if err == nil {
				err = element.Input(fieldValue)
			}

			if err != nil {
				fieldResult.Error = fmt.Sprintf("failed to fill field: %v", err)
				result.ErrorCount++
			} else {
				fieldResult.Success = true
				result.SuccessCount++
			}
		}

		result.FilledFields = append(result.FilledFields, fieldResult)
	}

	return result, nil
}

// PageInfo represents page metadata and state information
type PageInfo struct {
	Title         string `json:"title"`
	URL           string `json:"url"`
	LoadingState  string `json:"loading_state"`
	ReadyState    string `json:"ready_state"`
	Referrer      string `json:"referrer"`
	Domain        string `json:"domain"`
	Protocol      string `json:"protocol"`
	Charset       string `json:"charset"`
	ContentType   string `json:"content_type"`
	LastModified  string `json:"last_modified"`
	CookieEnabled bool   `json:"cookie_enabled"`
	OnlineStatus  bool   `json:"online_status"`
}

// ViewportInfo represents viewport and scroll information
type ViewportInfo struct {
	Width            int     `json:"width"`
	Height           int     `json:"height"`
	ScrollX          int     `json:"scroll_x"`
	ScrollY          int     `json:"scroll_y"`
	ScrollWidth      int     `json:"scroll_width"`
	ScrollHeight     int     `json:"scroll_height"`
	ClientWidth      int     `json:"client_width"`
	ClientHeight     int     `json:"client_height"`
	DevicePixelRatio float64 `json:"device_pixel_ratio"`
	Orientation      string  `json:"orientation"`
}

// PerformanceMetrics represents page performance data
type PerformanceMetrics struct {
	NavigationStart      int64 `json:"navigation_start"`
	LoadEventEnd         int64 `json:"load_event_end"`
	DOMContentLoaded     int64 `json:"dom_content_loaded"`
	FirstPaint           int64 `json:"first_paint"`
	FirstContentfulPaint int64 `json:"first_contentful_paint"`
	LoadTime             int64 `json:"load_time"`
	DOMLoadTime          int64 `json:"dom_load_time"`
	ResourceCount        int   `json:"resource_count"`
	JSHeapSizeLimit      int64 `json:"js_heap_size_limit"`
	JSHeapSizeTotal      int64 `json:"js_heap_size_total"`
	JSHeapSizeUsed       int64 `json:"js_heap_size_used"`
}

// ContentCheck represents content verification results
type ContentCheck struct {
	Type           string   `json:"type"`
	ImagesLoaded   int      `json:"images_loaded,omitempty"`
	ImagesTotal    int      `json:"images_total,omitempty"`
	ScriptsLoaded  int      `json:"scripts_loaded,omitempty"`
	ScriptsTotal   int      `json:"scripts_total,omitempty"`
	StylesLoaded   int      `json:"styles_loaded,omitempty"`
	StylesTotal    int      `json:"styles_total,omitempty"`
	FormsPresent   int      `json:"forms_present,omitempty"`
	LinksPresent   int      `json:"links_present,omitempty"`
	IframesPresent int      `json:"iframes_present,omitempty"`
	HasErrors      bool     `json:"has_errors,omitempty"`
	ErrorCount     int      `json:"error_count,omitempty"`
	ErrorMessages  []string `json:"error_messages,omitempty"`
}

// getPageInfo retrieves comprehensive page metadata and state information
func (d *Daemon) getPageInfo(tabID string, timeout int) (*PageInfo, error) {
	d.debugLog("Getting page info for tab: %s with timeout: %d", tabID, timeout)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	result := &PageInfo{}

	// Get basic page information using JavaScript
	// Note: page.Eval expects a function expression, not an IIFE
	jsCode := `() => {
		return {
			title: document.title,
			url: window.location.href,
			readyState: document.readyState,
			referrer: document.referrer,
			domain: document.domain,
			protocol: window.location.protocol,
			charset: document.characterSet || document.charset,
			contentType: document.contentType,
			lastModified: document.lastModified,
			cookieEnabled: navigator.cookieEnabled,
			onlineStatus: navigator.onLine
		};
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("page info JavaScript returned nil result")
	}

	// Parse the JavaScript result
	if props := jsResult.Value.Map(); props != nil {
		if title, ok := props["title"]; ok && title.Str() != "" {
			result.Title = title.Str()
		}
		if url, ok := props["url"]; ok && url.Str() != "" {
			result.URL = url.Str()
		}
		if readyState, ok := props["readyState"]; ok && readyState.Str() != "" {
			result.ReadyState = readyState.Str()
		}
		if referrer, ok := props["referrer"]; ok && referrer.Str() != "" {
			result.Referrer = referrer.Str()
		}
		if domain, ok := props["domain"]; ok && domain.Str() != "" {
			result.Domain = domain.Str()
		}
		if protocol, ok := props["protocol"]; ok && protocol.Str() != "" {
			result.Protocol = protocol.Str()
		}
		if charset, ok := props["charset"]; ok && charset.Str() != "" {
			result.Charset = charset.Str()
		}
		if contentType, ok := props["contentType"]; ok && contentType.Str() != "" {
			result.ContentType = contentType.Str()
		}
		if lastModified, ok := props["lastModified"]; ok && lastModified.Str() != "" {
			result.LastModified = lastModified.Str()
		}
		if cookieEnabled, ok := props["cookieEnabled"]; ok {
			result.CookieEnabled = cookieEnabled.Bool()
		}
		if onlineStatus, ok := props["onlineStatus"]; ok {
			result.OnlineStatus = onlineStatus.Bool()
		}
	}

	// Determine loading state
	if result.ReadyState == "complete" {
		result.LoadingState = "complete"
	} else if result.ReadyState == "interactive" {
		result.LoadingState = "interactive"
	} else {
		result.LoadingState = "loading"
	}

	d.debugLog("Successfully retrieved page info for tab: %s", tabID)
	return result, nil
}

// getViewportInfo retrieves viewport and scroll information
func (d *Daemon) getViewportInfo(tabID string, timeout int) (*ViewportInfo, error) {
	d.debugLog("Getting viewport info for tab: %s with timeout: %d", tabID, timeout)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	result := &ViewportInfo{}

	// Get viewport and scroll information using JavaScript
	// Note: page.Eval expects a function expression, not an IIFE
	jsCode := `() => {
		return {
			width: window.innerWidth,
			height: window.innerHeight,
			scrollX: window.scrollX || window.pageXOffset,
			scrollY: window.scrollY || window.pageYOffset,
			scrollWidth: document.documentElement.scrollWidth,
			scrollHeight: document.documentElement.scrollHeight,
			clientWidth: document.documentElement.clientWidth,
			clientHeight: document.documentElement.clientHeight,
			devicePixelRatio: window.devicePixelRatio,
			orientation: screen.orientation ? screen.orientation.type : 'unknown'
		};
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("viewport info JavaScript returned nil result")
	}

	// Parse the JavaScript result
	if props := jsResult.Value.Map(); props != nil {
		if width, ok := props["width"]; ok {
			result.Width = int(width.Num())
		}
		if height, ok := props["height"]; ok {
			result.Height = int(height.Num())
		}
		if scrollX, ok := props["scrollX"]; ok {
			result.ScrollX = int(scrollX.Num())
		}
		if scrollY, ok := props["scrollY"]; ok {
			result.ScrollY = int(scrollY.Num())
		}
		if scrollWidth, ok := props["scrollWidth"]; ok {
			result.ScrollWidth = int(scrollWidth.Num())
		}
		if scrollHeight, ok := props["scrollHeight"]; ok {
			result.ScrollHeight = int(scrollHeight.Num())
		}
		if clientWidth, ok := props["clientWidth"]; ok {
			result.ClientWidth = int(clientWidth.Num())
		}
		if clientHeight, ok := props["clientHeight"]; ok {
			result.ClientHeight = int(clientHeight.Num())
		}
		if devicePixelRatio, ok := props["devicePixelRatio"]; ok {
			result.DevicePixelRatio = devicePixelRatio.Num()
		}
		if orientation, ok := props["orientation"]; ok && orientation.Str() != "" {
			result.Orientation = orientation.Str()
		}
	}

	d.debugLog("Successfully retrieved viewport info for tab: %s", tabID)
	return result, nil
}

// getPerformance retrieves page performance metrics
func (d *Daemon) getPerformance(tabID string, timeout int) (*PerformanceMetrics, error) {
	d.debugLog("Getting performance metrics for tab: %s with timeout: %d", tabID, timeout)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	result := &PerformanceMetrics{}

	// Get performance metrics using JavaScript
	// Note: page.Eval expects a function expression, not an IIFE
	jsCode := `() => {
		const perf = window.performance;
		const timing = perf.timing;
		const navigation = perf.navigation;
		const memory = perf.memory;

		// Get paint metrics if available
		let firstPaint = 0;
		let firstContentfulPaint = 0;
		if (perf.getEntriesByType) {
			const paintEntries = perf.getEntriesByType('paint');
			for (const entry of paintEntries) {
				if (entry.name === 'first-paint') {
					firstPaint = entry.startTime;
				} else if (entry.name === 'first-contentful-paint') {
					firstContentfulPaint = entry.startTime;
				}
			}
		}

		// Count resources
		let resourceCount = 0;
		if (perf.getEntriesByType) {
			resourceCount = perf.getEntriesByType('resource').length;
		}

		return {
			navigationStart: timing.navigationStart,
			loadEventEnd: timing.loadEventEnd,
			domContentLoaded: timing.domContentLoadedEventEnd,
			firstPaint: firstPaint,
			firstContentfulPaint: firstContentfulPaint,
			loadTime: timing.loadEventEnd - timing.navigationStart,
			domLoadTime: timing.domContentLoadedEventEnd - timing.navigationStart,
			resourceCount: resourceCount,
			jsHeapSizeLimit: memory ? memory.jsHeapSizeLimit : 0,
			jsHeapSizeTotal: memory ? memory.totalJSHeapSize : 0,
			jsHeapSizeUsed: memory ? memory.usedJSHeapSize : 0
		};
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("performance metrics JavaScript returned nil result")
	}

	// Parse the JavaScript result
	if props := jsResult.Value.Map(); props != nil {
		if navigationStart, ok := props["navigationStart"]; ok {
			result.NavigationStart = int64(navigationStart.Num())
		}
		if loadEventEnd, ok := props["loadEventEnd"]; ok {
			result.LoadEventEnd = int64(loadEventEnd.Num())
		}
		if domContentLoaded, ok := props["domContentLoaded"]; ok {
			result.DOMContentLoaded = int64(domContentLoaded.Num())
		}
		if firstPaint, ok := props["firstPaint"]; ok {
			result.FirstPaint = int64(firstPaint.Num())
		}
		if firstContentfulPaint, ok := props["firstContentfulPaint"]; ok {
			result.FirstContentfulPaint = int64(firstContentfulPaint.Num())
		}
		if loadTime, ok := props["loadTime"]; ok {
			result.LoadTime = int64(loadTime.Num())
		}
		if domLoadTime, ok := props["domLoadTime"]; ok {
			result.DOMLoadTime = int64(domLoadTime.Num())
		}
		if resourceCount, ok := props["resourceCount"]; ok {
			result.ResourceCount = int(resourceCount.Num())
		}
		if jsHeapSizeLimit, ok := props["jsHeapSizeLimit"]; ok {
			result.JSHeapSizeLimit = int64(jsHeapSizeLimit.Num())
		}
		if jsHeapSizeTotal, ok := props["jsHeapSizeTotal"]; ok {
			result.JSHeapSizeTotal = int64(jsHeapSizeTotal.Num())
		}
		if jsHeapSizeUsed, ok := props["jsHeapSizeUsed"]; ok {
			result.JSHeapSizeUsed = int64(jsHeapSizeUsed.Num())
		}
	}

	d.debugLog("Successfully retrieved performance metrics for tab: %s", tabID)
	return result, nil
}

// checkContent verifies specific content types and loading states
func (d *Daemon) checkContent(tabID string, contentType string, timeout int) (*ContentCheck, error) {
	d.debugLog("Checking content type '%s' for tab: %s with timeout: %d", contentType, tabID, timeout)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	result := &ContentCheck{
		Type: contentType,
	}

	var jsCode string

	switch contentType {
	case "images":
		// Note: page.Eval expects a function expression, not an IIFE
		jsCode = `() => {
			const images = document.querySelectorAll('img');
			let loaded = 0;
			let total = images.length;

			images.forEach(img => {
				if (img.complete && img.naturalHeight !== 0) {
					loaded++;
				}
			});

			return {
				imagesLoaded: loaded,
				imagesTotal: total
			};
		}`
	case "scripts":
		jsCode = `() => {
			const scripts = document.querySelectorAll('script[src]');
			let loaded = 0;
			let total = scripts.length;

			scripts.forEach(script => {
				if (script.readyState === 'loaded' || script.readyState === 'complete' || !script.readyState) {
					loaded++;
				}
			});

			return {
				scriptsLoaded: loaded,
				scriptsTotal: total
			};
		}`
	case "styles":
		jsCode = `() => {
			const styles = document.querySelectorAll('link[rel="stylesheet"]');
			let loaded = 0;
			let total = styles.length;

			styles.forEach(style => {
				if (style.sheet) {
					loaded++;
				}
			});

			return {
				stylesLoaded: loaded,
				stylesTotal: total
			};
		}`
	case "forms":
		jsCode = `() => {
			return {
				formsPresent: document.querySelectorAll('form').length
			};
		}`
	case "links":
		jsCode = `() => {
			return {
				linksPresent: document.querySelectorAll('a[href]').length
			};
		}`
	case "iframes":
		jsCode = `() => {
			return {
				iframesPresent: document.querySelectorAll('iframe').length
			};
		}`
	case "errors":
		jsCode = `() => {
			const errors = [];

			// Check for JavaScript errors in console (if available)
			if (window.console && window.console.error) {
				// This is limited - we can't access console history
				// But we can check for common error indicators
			}

			// Check for broken images
			const brokenImages = Array.from(document.querySelectorAll('img')).filter(img =>
				!img.complete || img.naturalHeight === 0
			);

			if (brokenImages.length > 0) {
				errors.push('Broken images detected: ' + brokenImages.length);
			}

			// Check for missing stylesheets
			const brokenStyles = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(link =>
				!link.sheet
			);

			if (brokenStyles.length > 0) {
				errors.push('Missing stylesheets detected: ' + brokenStyles.length);
			}

			return {
				hasErrors: errors.length > 0,
				errorCount: errors.length,
				errorMessages: errors
			};
		}`
	default:
		return nil, fmt.Errorf("unknown content type: %s", contentType)
	}

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("content check JavaScript returned nil result")
	}

	// Parse the JavaScript result
	if props := jsResult.Value.Map(); props != nil {
		if imagesLoaded, ok := props["imagesLoaded"]; ok {
			result.ImagesLoaded = int(imagesLoaded.Num())
		}
		if imagesTotal, ok := props["imagesTotal"]; ok {
			result.ImagesTotal = int(imagesTotal.Num())
		}
		if scriptsLoaded, ok := props["scriptsLoaded"]; ok {
			result.ScriptsLoaded = int(scriptsLoaded.Num())
		}
		if scriptsTotal, ok := props["scriptsTotal"]; ok {
			result.ScriptsTotal = int(scriptsTotal.Num())
		}
		if stylesLoaded, ok := props["stylesLoaded"]; ok {
			result.StylesLoaded = int(stylesLoaded.Num())
		}
		if stylesTotal, ok := props["stylesTotal"]; ok {
			result.StylesTotal = int(stylesTotal.Num())
		}
		if formsPresent, ok := props["formsPresent"]; ok {
			result.FormsPresent = int(formsPresent.Num())
		}
		if linksPresent, ok := props["linksPresent"]; ok {
			result.LinksPresent = int(linksPresent.Num())
		}
		if iframesPresent, ok := props["iframesPresent"]; ok {
			result.IframesPresent = int(iframesPresent.Num())
		}
		if hasErrors, ok := props["hasErrors"]; ok {
			result.HasErrors = hasErrors.Bool()
		}
		if errorCount, ok := props["errorCount"]; ok {
			result.ErrorCount = int(errorCount.Num())
		}
		if errorMessages, ok := props["errorMessages"]; ok {
			if arr := errorMessages.Arr(); arr != nil {
				for _, msg := range arr {
					if msg.Str() != "" {
						result.ErrorMessages = append(result.ErrorMessages, msg.Str())
					}
				}
			}
		}
	}

	d.debugLog("Successfully checked content type '%s' for tab: %s", contentType, tabID)
	return result, nil
}

// screenshotElement takes a screenshot of a specific element
func (d *Daemon) screenshotElement(tabID, selector, outputPath string, timeout int) error {
	d.debugLog("Taking element screenshot for tab: %s, selector: %s", tabID, selector)

	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element
	var element *rod.Element
	if timeout > 0 {
		element, err = page.Timeout(time.Duration(timeout) * time.Second).Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element (timeout after %ds): %w", timeout, err)
		}
	} else {
		element, err = page.Element(selector)
		if err != nil {
			return fmt.Errorf("failed to find element: %w", err)
		}
	}

	// Scroll element into view
	err = element.ScrollIntoView()
	if err != nil {
		return fmt.Errorf("failed to scroll element into view: %w", err)
	}

	// Wait for element to be stable
	err = element.WaitStable(500 * time.Millisecond)
	if err != nil {
		d.debugLog("Warning: element not stable: %v", err)
	}

	// Take screenshot of the element
	screenshotBytes, err := element.Screenshot(proto.PageCaptureScreenshotFormatPng, 0)
	if err != nil {
		return fmt.Errorf("failed to capture element screenshot: %w", err)
	}

	// Write the screenshot to file
	err = os.WriteFile(outputPath, screenshotBytes, 0644)
	if err != nil {
		return fmt.Errorf("failed to save element screenshot to %s: %w", outputPath, err)
	}

	d.debugLog("Successfully captured element screenshot for tab: %s", tabID)
	return nil
}

// ScreenshotMetadata represents metadata for enhanced screenshots
type ScreenshotMetadata struct {
	Timestamp    string `json:"timestamp"`
	URL          string `json:"url"`
	Title        string `json:"title"`
	ViewportSize struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	} `json:"viewport_size"`
	FullPage   bool   `json:"full_page"`
	FilePath   string `json:"file_path"`
	FileSize   int64  `json:"file_size"`
	Resolution struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	} `json:"resolution"`
}

// screenshotEnhanced takes a screenshot with metadata
func (d *Daemon) screenshotEnhanced(tabID, outputPath string, fullPage bool, timeout int) (*ScreenshotMetadata, error) {
	d.debugLog("Taking enhanced screenshot for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// Get page info for metadata
	pageInfo, err := page.Info()
	if err != nil {
		return nil, fmt.Errorf("failed to get page info: %w", err)
	}

	// Get viewport size
	viewport, err := page.Eval(`() => ({
		width: window.innerWidth,
		height: window.innerHeight
	})`)
	if err != nil {
		return nil, fmt.Errorf("failed to get viewport: %w", err)
	}
	if viewport == nil {
		return nil, fmt.Errorf("viewport evaluation returned nil result")
	}

	viewportData := viewport.Value.Map()
	viewportWidth := int(viewportData["width"].Num())
	viewportHeight := int(viewportData["height"].Num())

	// Take screenshot with timeout handling
	var screenshotBytes []byte
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan error, 1)
		go func() {
			bytes, err := page.Screenshot(fullPage, &proto.PageCaptureScreenshot{
				Format: proto.PageCaptureScreenshotFormatPng,
			})
			screenshotBytes = bytes
			done <- err
		}()

		select {
		case err := <-done:
			if err != nil {
				return nil, fmt.Errorf("failed to capture screenshot: %w", err)
			}
		case <-ctx.Done():
			return nil, fmt.Errorf("taking screenshot timed out after %d seconds", timeout)
		}
	} else {
		screenshotBytes, err = page.Screenshot(fullPage, &proto.PageCaptureScreenshot{
			Format: proto.PageCaptureScreenshotFormatPng,
		})
		if err != nil {
			return nil, fmt.Errorf("failed to capture screenshot: %w", err)
		}
	}

	// Write the screenshot to file
	err = os.WriteFile(outputPath, screenshotBytes, 0644)
	if err != nil {
		return nil, fmt.Errorf("failed to save screenshot to %s: %w", outputPath, err)
	}

	// Get file info
	fileInfo, err := os.Stat(outputPath)
	if err != nil {
		return nil, fmt.Errorf("failed to get file info: %w", err)
	}

	// Create metadata
	metadata := &ScreenshotMetadata{
		Timestamp: time.Now().Format(time.RFC3339),
		URL:       pageInfo.URL,
		Title:     pageInfo.Title,
		FullPage:  fullPage,
		FilePath:  outputPath,
		FileSize:  fileInfo.Size(),
	}

	metadata.ViewportSize.Width = viewportWidth
	metadata.ViewportSize.Height = viewportHeight

	// Get actual image dimensions (approximate based on viewport or full page)
	if fullPage {
		// For full page, we'd need to calculate the full document size
		// For now, use viewport size as approximation
		metadata.Resolution.Width = viewportWidth
		metadata.Resolution.Height = viewportHeight
	} else {
		metadata.Resolution.Width = viewportWidth
		metadata.Resolution.Height = viewportHeight
	}

	d.debugLog("Successfully captured enhanced screenshot for tab: %s", tabID)
	return metadata, nil
}

// FileOperation represents a single file operation
type FileOperation struct {
	LocalPath     string `json:"local_path"`
	ContainerPath string `json:"container_path"`
	Operation     string `json:"operation"` // "upload" or "download"
}

// BulkFileResult represents the result of bulk file operations
type BulkFileResult struct {
	Successful []FileOperationResult `json:"successful"`
	Failed     []FileOperationError  `json:"failed"`
	Summary    struct {
		Total      int `json:"total"`
		Successful int `json:"successful"`
		Failed     int `json:"failed"`
	} `json:"summary"`
}

// FileOperationResult represents a successful file operation
type FileOperationResult struct {
	LocalPath     string `json:"local_path"`
	ContainerPath string `json:"container_path"`
	Operation     string `json:"operation"`
	Size          int64  `json:"size"`
}

// FileOperationError represents a failed file operation
type FileOperationError struct {
	LocalPath     string `json:"local_path"`
	ContainerPath string `json:"container_path"`
	Operation     string `json:"operation"`
	Error         string `json:"error"`
}

// bulkFiles performs bulk file operations (upload/download)
func (d *Daemon) bulkFiles(operationType, filesJSON string, timeout int) (*BulkFileResult, error) {
	d.debugLog("Performing bulk file operations: %s", operationType)

	// Parse the files JSON
	var operations []FileOperation
	err := json.Unmarshal([]byte(filesJSON), &operations)
	if err != nil {
		return nil, fmt.Errorf("failed to parse files JSON: %w", err)
	}

	result := &BulkFileResult{
		Successful: make([]FileOperationResult, 0),
		Failed:     make([]FileOperationError, 0),
	}

	// Set up timeout context
	ctx := context.Background()
	if timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
		defer cancel()
	}

	// Process each file operation
	for _, op := range operations {
		select {
		case <-ctx.Done():
			// Timeout reached, add remaining operations as failed
			for i := len(result.Successful) + len(result.Failed); i < len(operations); i++ {
				result.Failed = append(result.Failed, FileOperationError{
					LocalPath:     operations[i].LocalPath,
					ContainerPath: operations[i].ContainerPath,
					Operation:     operations[i].Operation,
					Error:         "operation timed out",
				})
			}
			break
		default:
			// Perform the operation
			if op.Operation == "upload" || (op.Operation == "" && operationType == "upload") {
				err := d.performFileUpload(op.LocalPath, op.ContainerPath)
				if err != nil {
					result.Failed = append(result.Failed, FileOperationError{
						LocalPath:     op.LocalPath,
						ContainerPath: op.ContainerPath,
						Operation:     "upload",
						Error:         err.Error(),
					})
				} else {
					// Get file size
					fileInfo, _ := os.Stat(op.ContainerPath)
					size := int64(0)
					if fileInfo != nil {
						size = fileInfo.Size()
					}
					result.Successful = append(result.Successful, FileOperationResult{
						LocalPath:     op.LocalPath,
						ContainerPath: op.ContainerPath,
						Operation:     "upload",
						Size:          size,
					})
				}
			} else if op.Operation == "download" || (op.Operation == "" && operationType == "download") {
				err := d.performFileDownload(op.ContainerPath, op.LocalPath)
				if err != nil {
					result.Failed = append(result.Failed, FileOperationError{
						LocalPath:     op.LocalPath,
						ContainerPath: op.ContainerPath,
						Operation:     "download",
						Error:         err.Error(),
					})
				} else {
					// Get file size
					fileInfo, _ := os.Stat(op.LocalPath)
					size := int64(0)
					if fileInfo != nil {
						size = fileInfo.Size()
					}
					result.Successful = append(result.Successful, FileOperationResult{
						LocalPath:     op.LocalPath,
						ContainerPath: op.ContainerPath,
						Operation:     "download",
						Size:          size,
					})
				}
			} else {
				result.Failed = append(result.Failed, FileOperationError{
					LocalPath:     op.LocalPath,
					ContainerPath: op.ContainerPath,
					Operation:     op.Operation,
					Error:         "unknown operation type",
				})
			}
		}
	}

	// Update summary
	result.Summary.Total = len(operations)
	result.Summary.Successful = len(result.Successful)
	result.Summary.Failed = len(result.Failed)

	d.debugLog("Bulk file operations completed: %d successful, %d failed", result.Summary.Successful, result.Summary.Failed)
	return result, nil
}

// performFileUpload handles a single file upload operation
func (d *Daemon) performFileUpload(localPath, containerPath string) error {
	// Open the source file
	sourceFile, err := os.Open(localPath)
	if err != nil {
		return fmt.Errorf("failed to open source file: %w", err)
	}
	defer sourceFile.Close()

	// Create the destination file
	destFile, err := os.Create(containerPath)
	if err != nil {
		return fmt.Errorf("failed to create destination file: %w", err)
	}
	defer destFile.Close()

	// Copy the file
	_, err = io.Copy(destFile, sourceFile)
	if err != nil {
		return fmt.Errorf("failed to copy file: %w", err)
	}

	return nil
}

// performFileDownload handles a single file download operation
func (d *Daemon) performFileDownload(containerPath, localPath string) error {
	// Open the source file
	sourceFile, err := os.Open(containerPath)
	if err != nil {
		return fmt.Errorf("failed to open source file: %w", err)
	}
	defer sourceFile.Close()

	// Create the destination file
	destFile, err := os.Create(localPath)
	if err != nil {
		return fmt.Errorf("failed to create destination file: %w", err)
	}
	defer destFile.Close()

	// Copy the file
	_, err = io.Copy(destFile, sourceFile)
	if err != nil {
		return fmt.Errorf("failed to copy file: %w", err)
	}

	return nil
}

// FileManagementResult represents the result of file management operations
type FileManagementResult struct {
	Operation string                 `json:"operation"`
	Files     []FileInfo             `json:"files,omitempty"`
	Cleaned   []string               `json:"cleaned,omitempty"`
	Summary   map[string]interface{} `json:"summary"`
}

// FileInfo represents information about a file
type FileInfo struct {
	Path        string    `json:"path"`
	Size        int64     `json:"size"`
	ModTime     time.Time `json:"mod_time"`
	IsDir       bool      `json:"is_dir"`
	Permissions string    `json:"permissions"`
}

// manageFiles performs file management operations
func (d *Daemon) manageFiles(operation, pattern, maxAge string) (*FileManagementResult, error) {
	d.debugLog("Performing file management operation: %s", operation)

	result := &FileManagementResult{
		Operation: operation,
		Summary:   make(map[string]interface{}),
	}

	switch operation {
	case "cleanup":
		return d.cleanupFiles(pattern, maxAge, result)
	case "list":
		return d.listFiles(pattern, result)
	case "info":
		return d.getFileInfo(pattern, result)
	default:
		return nil, fmt.Errorf("unknown file management operation: %s", operation)
	}
}

// cleanupFiles removes files matching pattern and age criteria
func (d *Daemon) cleanupFiles(pattern, maxAge string, result *FileManagementResult) (*FileManagementResult, error) {
	// Parse max age (default to 24 hours if not specified)
	maxAgeHours := 24
	if maxAge != "" {
		if parsed, err := strconv.Atoi(maxAge); err == nil && parsed > 0 {
			maxAgeHours = parsed
		}
	}

	cutoffTime := time.Now().Add(-time.Duration(maxAgeHours) * time.Hour)

	// Default pattern if not specified
	if pattern == "" {
		pattern = "/tmp/cremote-*"
	}

	// Find files matching pattern
	matches, err := filepath.Glob(pattern)
	if err != nil {
		return nil, fmt.Errorf("failed to find files matching pattern: %w", err)
	}

	var cleaned []string
	var totalSize int64

	for _, filePath := range matches {
		fileInfo, err := os.Stat(filePath)
		if err != nil {
			continue // Skip files we can't stat
		}

		// Check if file is older than cutoff time
		if fileInfo.ModTime().Before(cutoffTime) {
			totalSize += fileInfo.Size()
			err = os.Remove(filePath)
			if err != nil {
				d.debugLog("Failed to remove file %s: %v", filePath, err)
			} else {
				cleaned = append(cleaned, filePath)
			}
		}
	}

	result.Cleaned = cleaned
	result.Summary["files_cleaned"] = len(cleaned)
	result.Summary["total_size_freed"] = totalSize
	result.Summary["cutoff_time"] = cutoffTime.Format(time.RFC3339)

	d.debugLog("Cleanup completed: %d files removed, %d bytes freed", len(cleaned), totalSize)
	return result, nil
}

// listFiles lists files matching pattern
func (d *Daemon) listFiles(pattern string, result *FileManagementResult) (*FileManagementResult, error) {
	// Default pattern if not specified
	if pattern == "" {
		pattern = "/tmp/*"
	}

	// Find files matching pattern
	matches, err := filepath.Glob(pattern)
	if err != nil {
		return nil, fmt.Errorf("failed to find files matching pattern: %w", err)
	}

	var files []FileInfo
	var totalSize int64

	for _, filePath := range matches {
		fileInfo, err := os.Stat(filePath)
		if err != nil {
			continue // Skip files we can't stat
		}

		files = append(files, FileInfo{
			Path:        filePath,
			Size:        fileInfo.Size(),
			ModTime:     fileInfo.ModTime(),
			IsDir:       fileInfo.IsDir(),
			Permissions: fileInfo.Mode().String(),
		})

		if !fileInfo.IsDir() {
			totalSize += fileInfo.Size()
		}
	}

	result.Files = files
	result.Summary["total_files"] = len(files)
	result.Summary["total_size"] = totalSize

	d.debugLog("Listed %d files matching pattern: %s", len(files), pattern)
	return result, nil
}

// getFileInfo gets detailed information about a specific file
func (d *Daemon) getFileInfo(filePath string, result *FileManagementResult) (*FileManagementResult, error) {
	if filePath == "" {
		return nil, fmt.Errorf("file path is required for info operation")
	}

	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to get file info: %w", err)
	}

	files := []FileInfo{{
		Path:        filePath,
		Size:        fileInfo.Size(),
		ModTime:     fileInfo.ModTime(),
		IsDir:       fileInfo.IsDir(),
		Permissions: fileInfo.Mode().String(),
	}}

	result.Files = files
	result.Summary["exists"] = true
	result.Summary["size"] = fileInfo.Size()
	result.Summary["is_directory"] = fileInfo.IsDir()
	result.Summary["last_modified"] = fileInfo.ModTime().Format(time.RFC3339)

	d.debugLog("Retrieved info for file: %s", filePath)
	return result, nil
}

// Accessibility tree data structures

// AXNode represents a node in the accessibility tree
type AXNode struct {
	NodeID           string       `json:"nodeId"`
	Ignored          bool         `json:"ignored"`
	IgnoredReasons   []AXProperty `json:"ignoredReasons,omitempty"`
	Role             *AXValue     `json:"role,omitempty"`
	ChromeRole       *AXValue     `json:"chromeRole,omitempty"`
	Name             *AXValue     `json:"name,omitempty"`
	Description      *AXValue     `json:"description,omitempty"`
	Value            *AXValue     `json:"value,omitempty"`
	Properties       []AXProperty `json:"properties,omitempty"`
	ParentID         string       `json:"parentId,omitempty"`
	ChildIDs         []string     `json:"childIds,omitempty"`
	BackendDOMNodeID int          `json:"backendDOMNodeId,omitempty"`
	FrameID          string       `json:"frameId,omitempty"`
}

// AXProperty represents a property of an accessibility node
type AXProperty struct {
	Name  string   `json:"name"`
	Value *AXValue `json:"value"`
}

// AXValue represents a computed accessibility value
type AXValue struct {
	Type         string          `json:"type"`
	Value        interface{}     `json:"value,omitempty"`
	RelatedNodes []AXRelatedNode `json:"relatedNodes,omitempty"`
	Sources      []AXValueSource `json:"sources,omitempty"`
}

// AXRelatedNode represents a related node in the accessibility tree
type AXRelatedNode struct {
	BackendDOMNodeID int    `json:"backendDOMNodeId"`
	IDRef            string `json:"idref,omitempty"`
	Text             string `json:"text,omitempty"`
}

// AXValueSource represents a source for a computed accessibility value
type AXValueSource struct {
	Type              string   `json:"type"`
	Value             *AXValue `json:"value,omitempty"`
	Attribute         string   `json:"attribute,omitempty"`
	AttributeValue    *AXValue `json:"attributeValue,omitempty"`
	Superseded        bool     `json:"superseded,omitempty"`
	NativeSource      string   `json:"nativeSource,omitempty"`
	NativeSourceValue *AXValue `json:"nativeSourceValue,omitempty"`
	Invalid           bool     `json:"invalid,omitempty"`
	InvalidReason     string   `json:"invalidReason,omitempty"`
}

// AccessibilityTreeResult represents the result of accessibility tree operations
type AccessibilityTreeResult struct {
	Nodes []AXNode `json:"nodes"`
}

// AccessibilityQueryResult represents the result of accessibility queries
type AccessibilityQueryResult struct {
	Nodes []AXNode `json:"nodes"`
}

// getAccessibilityTree retrieves the full accessibility tree for a tab
func (d *Daemon) getAccessibilityTree(tabID string, depth *int, timeout int) (*AccessibilityTreeResult, error) {
	return d.getAccessibilityTreeWithContrast(tabID, depth, false, timeout)
}

// getAccessibilityTreeWithContrast retrieves the full accessibility tree with optional contrast data
func (d *Daemon) getAccessibilityTreeWithContrast(tabID string, depth *int, includeContrast bool, timeout int) (*AccessibilityTreeResult, error) {
	d.debugLog("Getting accessibility tree for tab: %s with depth: %v, includeContrast: %v, timeout: %d", tabID, depth, includeContrast, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Enable accessibility domain
	err = proto.AccessibilityEnable{}.Call(page)
	if err != nil {
		return nil, fmt.Errorf("failed to enable accessibility domain: %v", err)
	}

	// Build the request parameters
	params := proto.AccessibilityGetFullAXTree{}
	if depth != nil {
		params.Depth = depth
	}

	// Call the Chrome DevTools Protocol Accessibility.getFullAXTree method
	result, err := proto.AccessibilityGetFullAXTree{}.Call(page)
	if err != nil {
		return nil, fmt.Errorf("failed to get accessibility tree: %v", err)
	}

	// Parse the result
	var axResult AccessibilityTreeResult
	for _, node := range result.Nodes {
		axNode := d.convertProtoAXNode(node)

		// Add contrast data if requested (simplified - just add a note that contrast checking is available)
		if includeContrast && !node.Ignored && node.BackendDOMNodeID > 0 {
			// Add a property indicating contrast data is available via web_contrast_check tool
			axNode.Properties = append(axNode.Properties, AXProperty{
				Name: "contrastCheckAvailable",
				Value: &AXValue{
					Type:  "boolean",
					Value: true,
				},
			})
		}

		axResult.Nodes = append(axResult.Nodes, axNode)
	}

	d.debugLog("Successfully retrieved accessibility tree with %d nodes for tab: %s", len(axResult.Nodes), tabID)
	return &axResult, nil
}

// convertProtoAXNode converts a proto.AccessibilityAXNode to our AXNode struct
func (d *Daemon) convertProtoAXNode(protoNode *proto.AccessibilityAXNode) AXNode {
	node := AXNode{
		NodeID:           string(protoNode.NodeID),
		Ignored:          protoNode.Ignored,
		BackendDOMNodeID: int(protoNode.BackendDOMNodeID),
	}

	// Convert role
	if protoNode.Role != nil {
		node.Role = d.convertProtoAXValue(protoNode.Role)
	}

	// Convert chrome role
	if protoNode.ChromeRole != nil {
		node.ChromeRole = d.convertProtoAXValue(protoNode.ChromeRole)
	}

	// Convert name
	if protoNode.Name != nil {
		node.Name = d.convertProtoAXValue(protoNode.Name)
	}

	// Convert description
	if protoNode.Description != nil {
		node.Description = d.convertProtoAXValue(protoNode.Description)
	}

	// Convert value
	if protoNode.Value != nil {
		node.Value = d.convertProtoAXValue(protoNode.Value)
	}

	// Convert properties
	for _, prop := range protoNode.Properties {
		node.Properties = append(node.Properties, AXProperty{
			Name:  string(prop.Name),
			Value: d.convertProtoAXValue(prop.Value),
		})
	}

	// Convert ignored reasons
	for _, reason := range protoNode.IgnoredReasons {
		node.IgnoredReasons = append(node.IgnoredReasons, AXProperty{
			Name:  string(reason.Name),
			Value: d.convertProtoAXValue(reason.Value),
		})
	}

	// Convert parent and child IDs
	if protoNode.ParentID != "" {
		node.ParentID = string(protoNode.ParentID)
	}

	for _, childID := range protoNode.ChildIDs {
		node.ChildIDs = append(node.ChildIDs, string(childID))
	}

	if protoNode.FrameID != "" {
		node.FrameID = string(protoNode.FrameID)
	}

	return node
}

// convertProtoAXValue converts a proto.AccessibilityAXValue to our AXValue struct
func (d *Daemon) convertProtoAXValue(protoValue *proto.AccessibilityAXValue) *AXValue {
	if protoValue == nil {
		return nil
	}

	value := &AXValue{
		Type:  string(protoValue.Type),
		Value: protoValue.Value,
	}

	// Convert related nodes
	for _, relatedNode := range protoValue.RelatedNodes {
		value.RelatedNodes = append(value.RelatedNodes, AXRelatedNode{
			BackendDOMNodeID: int(relatedNode.BackendDOMNodeID),
			IDRef:            relatedNode.Idref,
			Text:             relatedNode.Text,
		})
	}

	// Convert sources
	for _, source := range protoValue.Sources {
		axSource := AXValueSource{
			Type:          string(source.Type),
			Superseded:    source.Superseded,
			Invalid:       source.Invalid,
			InvalidReason: source.InvalidReason,
		}

		if source.Value != nil {
			axSource.Value = d.convertProtoAXValue(source.Value)
		}

		if source.Attribute != "" {
			axSource.Attribute = source.Attribute
		}

		if source.AttributeValue != nil {
			axSource.AttributeValue = d.convertProtoAXValue(source.AttributeValue)
		}

		if source.NativeSource != "" {
			axSource.NativeSource = string(source.NativeSource)
		}

		if source.NativeSourceValue != nil {
			axSource.NativeSourceValue = d.convertProtoAXValue(source.NativeSourceValue)
		}

		value.Sources = append(value.Sources, axSource)
	}

	return value
}

// getPartialAccessibilityTree retrieves a partial accessibility tree for a specific element
func (d *Daemon) getPartialAccessibilityTree(tabID, selector string, fetchRelatives bool, timeout int) (*AccessibilityTreeResult, error) {
	d.debugLog("Getting partial accessibility tree for tab: %s, selector: %s, fetchRelatives: %v, timeout: %d", tabID, selector, fetchRelatives, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Enable accessibility domain
	err = proto.AccessibilityEnable{}.Call(page)
	if err != nil {
		return nil, fmt.Errorf("failed to enable accessibility domain: %v", err)
	}

	// Find the DOM node first
	var element *rod.Element
	if timeout > 0 {
		element, err = page.Timeout(time.Duration(timeout) * time.Second).Element(selector)
	} else {
		element, err = page.Element(selector)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to find element: %w", err)
	}

	// Get the backend node ID
	nodeInfo, err := element.Describe(1, false)
	if err != nil {
		return nil, fmt.Errorf("failed to describe element: %w", err)
	}

	// Call the Chrome DevTools Protocol Accessibility.getPartialAXTree method
	result, err := proto.AccessibilityGetPartialAXTree{
		BackendNodeID:  nodeInfo.BackendNodeID,
		FetchRelatives: fetchRelatives,
	}.Call(page)
	if err != nil {
		return nil, fmt.Errorf("failed to get partial accessibility tree: %v", err)
	}

	// Parse the result
	var axResult AccessibilityTreeResult
	for _, node := range result.Nodes {
		axNode := d.convertProtoAXNode(node)
		axResult.Nodes = append(axResult.Nodes, axNode)
	}

	d.debugLog("Successfully retrieved partial accessibility tree with %d nodes for tab: %s", len(axResult.Nodes), tabID)
	return &axResult, nil
}

// queryAccessibilityTree queries the accessibility tree for nodes matching specific criteria
func (d *Daemon) queryAccessibilityTree(tabID, selector, accessibleName, role string, timeout int) (*AccessibilityQueryResult, error) {
	d.debugLog("Querying accessibility tree for tab: %s, selector: %s, name: %s, role: %s, timeout: %d", tabID, selector, accessibleName, role, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return nil, fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Enable accessibility domain
	err = proto.AccessibilityEnable{}.Call(page)
	if err != nil {
		return nil, fmt.Errorf("failed to enable accessibility domain: %v", err)
	}

	// Find the DOM node first if selector is provided
	var backendNodeID *proto.DOMBackendNodeID
	if selector != "" {
		var element *rod.Element
		if timeout > 0 {
			element, err = page.Timeout(time.Duration(timeout) * time.Second).Element(selector)
		} else {
			element, err = page.Element(selector)
		}
		if err != nil {
			return nil, fmt.Errorf("failed to find element: %w", err)
		}

		// Get the backend node ID
		nodeInfo, err := element.Describe(1, false)
		if err != nil {
			return nil, fmt.Errorf("failed to describe element: %w", err)
		}
		backendNodeID = &nodeInfo.BackendNodeID
	}

	// Build query parameters
	queryParams := proto.AccessibilityQueryAXTree{}
	if backendNodeID != nil {
		queryParams.BackendNodeID = *backendNodeID
	}
	if accessibleName != "" {
		queryParams.AccessibleName = accessibleName
	}
	if role != "" {
		queryParams.Role = role
	}

	// Call the Chrome DevTools Protocol Accessibility.queryAXTree method
	result, err := queryParams.Call(page)
	if err != nil {
		return nil, fmt.Errorf("failed to query accessibility tree: %v", err)
	}

	// Parse the result
	var axResult AccessibilityQueryResult
	for _, node := range result.Nodes {
		axNode := d.convertProtoAXNode(node)
		axResult.Nodes = append(axResult.Nodes, axNode)
	}

	d.debugLog("Successfully queried accessibility tree with %d matching nodes for tab: %s", len(axResult.Nodes), tabID)
	return &axResult, nil
}

// setCacheDisabled enables or disables browser cache for a tab
func (d *Daemon) setCacheDisabled(tabID string, disabled bool, timeout int) error {
	d.debugLog("Setting cache disabled=%v for tab: %s with timeout: %d", disabled, tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the cache setting in a goroutine
		go func() {
			err := proto.NetworkSetCacheDisabled{CacheDisabled: disabled}.Call(page)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to set cache disabled: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout setting cache disabled after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := proto.NetworkSetCacheDisabled{CacheDisabled: disabled}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to set cache disabled: %v", err)
		}
	}

	d.debugLog("Successfully set cache disabled=%v for tab: %s", disabled, tabID)
	return nil
}

// clearBrowserCache clears the browser cache for a tab
func (d *Daemon) clearBrowserCache(tabID string, timeout int) error {
	d.debugLog("Clearing browser cache for tab: %s with timeout: %d", tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the cache clearing in a goroutine
		go func() {
			err := proto.NetworkClearBrowserCache{}.Call(page)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to clear browser cache: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout clearing browser cache after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := proto.NetworkClearBrowserCache{}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to clear browser cache: %v", err)
		}
	}

	d.debugLog("Successfully cleared browser cache for tab: %s", tabID)
	return nil
}

// clearAllSiteData clears all site data including cookies, storage, cache, etc. for a tab
func (d *Daemon) clearAllSiteData(tabID string, timeout int) error {
	d.debugLog("Clearing all site data for tab: %s with timeout: %d", tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the site data clearing in a goroutine
		go func() {
			// Clear all types of site data
			err := proto.StorageClearDataForOrigin{
				Origin:       "*", // Clear for all origins
				StorageTypes: "appcache,cookies,file_systems,indexeddb,local_storage,shader_cache,websql,service_workers,cache_storage",
			}.Call(page)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to clear all site data: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout clearing all site data after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := proto.StorageClearDataForOrigin{
			Origin:       "*", // Clear for all origins
			StorageTypes: "appcache,cookies,file_systems,indexeddb,local_storage,shader_cache,websql,service_workers,cache_storage",
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to clear all site data: %v", err)
		}
	}

	d.debugLog("Successfully cleared all site data for tab: %s", tabID)
	return nil
}

// clearCookies clears cookies for a tab
func (d *Daemon) clearCookies(tabID string, timeout int) error {
	d.debugLog("Clearing cookies for tab: %s with timeout: %d", tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the cookie clearing in a goroutine
		go func() {
			// Clear cookies only
			err := proto.StorageClearDataForOrigin{
				Origin:       "*", // Clear for all origins
				StorageTypes: "cookies",
			}.Call(page)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to clear cookies: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout clearing cookies after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := proto.StorageClearDataForOrigin{
			Origin:       "*", // Clear for all origins
			StorageTypes: "cookies",
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to clear cookies: %v", err)
		}
	}

	d.debugLog("Successfully cleared cookies for tab: %s", tabID)
	return nil
}

// clearStorage clears web storage (localStorage, sessionStorage, IndexedDB, etc.) for a tab
func (d *Daemon) clearStorage(tabID string, timeout int) error {
	d.debugLog("Clearing storage for tab: %s with timeout: %d", tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the storage clearing in a goroutine
		go func() {
			// Clear storage types (excluding cookies and cache)
			err := proto.StorageClearDataForOrigin{
				Origin:       "*", // Clear for all origins
				StorageTypes: "appcache,file_systems,indexeddb,local_storage,websql,service_workers,cache_storage",
			}.Call(page)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to clear storage: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout clearing storage after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := proto.StorageClearDataForOrigin{
			Origin:       "*", // Clear for all origins
			StorageTypes: "appcache,file_systems,indexeddb,local_storage,websql,service_workers,cache_storage",
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to clear storage: %v", err)
		}
	}

	d.debugLog("Successfully cleared storage for tab: %s", tabID)
	return nil
}

// dragAndDrop performs a drag and drop operation from source element to target element
func (d *Daemon) dragAndDrop(tabID, sourceSelector, targetSelector string, timeout int) error {
	d.debugLog("Performing drag and drop from %s to %s for tab: %s with timeout: %d", sourceSelector, targetSelector, tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the drag and drop in a goroutine
		go func() {
			err := d.performDragAndDrop(page, sourceSelector, targetSelector)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to perform drag and drop: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout performing drag and drop after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := d.performDragAndDrop(page, sourceSelector, targetSelector)
		if err != nil {
			return fmt.Errorf("failed to perform drag and drop: %v", err)
		}
	}

	d.debugLog("Successfully performed drag and drop for tab: %s", tabID)
	return nil
}

// dragAndDropToCoordinates performs a drag and drop operation from source element to specific coordinates
func (d *Daemon) dragAndDropToCoordinates(tabID, sourceSelector string, targetX, targetY, timeout int) error {
	d.debugLog("Performing drag and drop from %s to coordinates (%d, %d) for tab: %s with timeout: %d", sourceSelector, targetX, targetY, tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the drag and drop in a goroutine
		go func() {
			err := d.performDragAndDropToCoordinates(page, sourceSelector, targetX, targetY)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to perform drag and drop to coordinates: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout performing drag and drop to coordinates after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := d.performDragAndDropToCoordinates(page, sourceSelector, targetX, targetY)
		if err != nil {
			return fmt.Errorf("failed to perform drag and drop to coordinates: %v", err)
		}
	}

	d.debugLog("Successfully performed drag and drop to coordinates for tab: %s", tabID)
	return nil
}

// dragAndDropByOffset performs a drag and drop operation from source element by a relative offset
func (d *Daemon) dragAndDropByOffset(tabID, sourceSelector string, offsetX, offsetY, timeout int) error {
	d.debugLog("Performing drag and drop from %s by offset (%d, %d) for tab: %s with timeout: %d", sourceSelector, offsetX, offsetY, tabID, timeout)

	// Use current tab if not specified
	if tabID == "" {
		tabID = d.currentTab
	}

	if tabID == "" {
		return fmt.Errorf("no tab specified and no current tab available")
	}

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Create a context with timeout if specified
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		// Create a channel to signal completion
		done := make(chan error, 1)

		// Execute the drag and drop in a goroutine
		go func() {
			err := d.performDragAndDropByOffset(page, sourceSelector, offsetX, offsetY)
			done <- err
		}()

		// Wait for completion or timeout
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to perform drag and drop by offset: %v", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("timeout performing drag and drop by offset after %d seconds", timeout)
		}
	} else {
		// No timeout - execute directly
		err := d.performDragAndDropByOffset(page, sourceSelector, offsetX, offsetY)
		if err != nil {
			return fmt.Errorf("failed to perform drag and drop by offset: %v", err)
		}
	}

	d.debugLog("Successfully performed drag and drop by offset for tab: %s", tabID)
	return nil
}

// performDragAndDrop performs the actual drag and drop operation between two elements
func (d *Daemon) performDragAndDrop(page *rod.Page, sourceSelector, targetSelector string) error {
	// First, try the enhanced HTML5 drag and drop approach
	err := d.performHTML5DragAndDrop(page, sourceSelector, targetSelector)
	if err == nil {
		d.debugLog("HTML5 drag and drop completed successfully")
		return nil
	}

	d.debugLog("HTML5 drag and drop failed (%v), falling back to mouse events", err)

	// Fallback to the original mouse-based approach
	// Find source element
	sourceElement, err := page.Element(sourceSelector)
	if err != nil {
		return fmt.Errorf("failed to find source element %s: %v", sourceSelector, err)
	}

	// Find target element
	targetElement, err := page.Element(targetSelector)
	if err != nil {
		return fmt.Errorf("failed to find target element %s: %v", targetSelector, err)
	}

	// Get source element position and size
	sourceBox, err := sourceElement.Shape()
	if err != nil {
		return fmt.Errorf("failed to get source element shape: %v", err)
	}

	// Get target element position and size
	targetBox, err := targetElement.Shape()
	if err != nil {
		return fmt.Errorf("failed to get target element shape: %v", err)
	}

	// Calculate center points from the first quad (border box)
	if len(sourceBox.Quads) == 0 {
		return fmt.Errorf("source element has no quads")
	}
	if len(targetBox.Quads) == 0 {
		return fmt.Errorf("target element has no quads")
	}

	sourceQuad := sourceBox.Quads[0]
	targetQuad := targetBox.Quads[0]

	// Calculate center from quad points (quad has 8 values: x1,y1,x2,y2,x3,y3,x4,y4)
	sourceX := (sourceQuad[0] + sourceQuad[2] + sourceQuad[4] + sourceQuad[6]) / 4
	sourceY := (sourceQuad[1] + sourceQuad[3] + sourceQuad[5] + sourceQuad[7]) / 4
	targetX := (targetQuad[0] + targetQuad[2] + targetQuad[4] + targetQuad[6]) / 4
	targetY := (targetQuad[1] + targetQuad[3] + targetQuad[5] + targetQuad[7]) / 4

	return d.performDragAndDropBetweenPoints(page, sourceX, sourceY, targetX, targetY)
}

// injectDragDropHelpers injects the JavaScript drag and drop helper functions into the page
func (d *Daemon) injectDragDropHelpers(page *rod.Page) error {
	// Read the JavaScript helper file
	jsHelpers := `
// HTML5 Drag and Drop Helper Functions for Cremote
// These functions are injected into web pages to provide reliable drag and drop functionality

(function() {
    'use strict';

    // Create a namespace to avoid conflicts
    window.cremoteDragDrop = window.cremoteDragDrop || {};

    /**
     * Simulates HTML5 drag and drop between two elements
     * @param {string} sourceSelector - CSS selector for source element
     * @param {string} targetSelector - CSS selector for target element
     * @returns {Promise<boolean>} - Success status
     */
    window.cremoteDragDrop.dragElementToElement = async function(sourceSelector, targetSelector) {
        const sourceElement = document.querySelector(sourceSelector);
        const targetElement = document.querySelector(targetSelector);

        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }
        if (!targetElement) {
            throw new Error('Target element not found: ' + targetSelector);
        }

        // Make source draggable if not already
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create and dispatch dragstart event
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: new DataTransfer()
        });

        // Set drag data
        dragStartEvent.dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dragStartEvent.dataTransfer.effectAllowed = 'all';

        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            console.log('Dragstart was cancelled');
            return false;
        }

        // Small delay to simulate realistic drag timing
        await new Promise(resolve => setTimeout(resolve, 50));

        // Create and dispatch dragover event on target
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });

        const dragOverResult = targetElement.dispatchEvent(dragOverEvent);

        // Create and dispatch drop event on target
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });

        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Create and dispatch dragend event on source
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });

        sourceElement.dispatchEvent(dragEndEvent);

        return dropResult;
    };

    console.log('Cremote drag and drop helpers loaded successfully');
})();
`

	// Inject the JavaScript helpers
	_, err := page.Eval(jsHelpers)
	if err != nil {
		return fmt.Errorf("failed to inject drag and drop helpers: %v", err)
	}

	return nil
}

// performHTML5DragAndDrop performs drag and drop using HTML5 drag events
func (d *Daemon) performHTML5DragAndDrop(page *rod.Page, sourceSelector, targetSelector string) error {
	// Inject the helper functions
	err := d.injectDragDropHelpers(page)
	if err != nil {
		return fmt.Errorf("failed to inject helpers: %v", err)
	}

	// Execute the HTML5 drag and drop
	jsCode := fmt.Sprintf(`
		(async function() {
			try {
				const result = await window.cremoteDragDrop.dragElementToElement('%s', '%s');
				return { success: result, error: null };
			} catch (error) {
				return { success: false, error: error.message };
			}
		})()
	`, sourceSelector, targetSelector)

	result, err := page.Eval(jsCode)
	if err != nil {
		return fmt.Errorf("failed to execute HTML5 drag and drop: %v", err)
	}

	// Parse the result
	resultMap := result.Value.Map()
	if resultMap == nil {
		return fmt.Errorf("invalid result from HTML5 drag and drop")
	}

	success, exists := resultMap["success"]
	if !exists || !success.Bool() {
		errorMsg := "unknown error"
		if errorVal, exists := resultMap["error"]; exists && errorVal.Str() != "" {
			errorMsg = errorVal.Str()
		}
		return fmt.Errorf("HTML5 drag and drop failed: %s", errorMsg)
	}

	return nil
}

// injectEnhancedDragDropHelpers injects the complete JavaScript drag and drop helper functions
func (d *Daemon) injectEnhancedDragDropHelpers(page *rod.Page) error {
	// Read the perfect JavaScript helper file content
	jsHelpers := `
// Perfect HTML5 Drag and Drop Helper Functions for Cremote
// These functions achieve 100% reliability for drag and drop operations

(function() {
    'use strict';

    // Create a namespace to avoid conflicts
    window.cremoteDragDrop = window.cremoteDragDrop || {};

    /**
     * Perfect HTML5 drag and drop between two elements
     * @param {string} sourceSelector - CSS selector for source element
     * @param {string} targetSelector - CSS selector for target element
     * @returns {Promise<boolean>} - Success status
     */
    window.cremoteDragDrop.dragElementToElement = async function(sourceSelector, targetSelector) {
        const sourceElement = document.querySelector(sourceSelector);
        const targetElement = document.querySelector(targetSelector);

        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }
        if (!targetElement) {
            throw new Error('Target element not found: ' + targetSelector);
        }

        // Ensure source is draggable
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create a persistent DataTransfer object
        const dataTransfer = new DataTransfer();
        dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dataTransfer.setData('application/x-cremote-drag', JSON.stringify({
            sourceId: sourceElement.id,
            sourceSelector: sourceSelector,
            timestamp: Date.now()
        }));
        dataTransfer.effectAllowed = 'all';

        // Step 1: Dispatch dragstart event
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });

        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            console.log('Dragstart was cancelled');
            return false;
        }

        // Step 2: Small delay for realism
        await new Promise(resolve => setTimeout(resolve, 50));

        // Step 3: Dispatch dragenter event on target
        const dragEnterEvent = new DragEvent('dragenter', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        targetElement.dispatchEvent(dragEnterEvent);

        // Step 4: Dispatch dragover event on target (critical for drop acceptance)
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });

        // Prevent default to allow drop
        dragOverEvent.preventDefault = function() { this.defaultPrevented = true; };
        const dragOverResult = targetElement.dispatchEvent(dragOverEvent);

        // Step 5: Dispatch drop event on target
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });

        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Step 6: Dispatch dragend event on source
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });

        sourceElement.dispatchEvent(dragEndEvent);

        return dropResult;
    };

    /**
     * Enhanced drop target detection with multiple strategies
     * @param {Element} element - Element to check
     * @returns {boolean} - Whether element can receive drops
     */
    window.cremoteDragDrop.hasDropEventListener = function(element) {
        // Strategy 1: Check for explicit drop handlers
        if (element.ondrop) return true;
        if (element.getAttribute('ondrop')) return true;

        // Strategy 2: Check for dragover handlers (indicates drop capability)
        if (element.ondragover || element.getAttribute('ondragover')) return true;

        // Strategy 3: Check for common drop zone indicators
        const dropIndicators = ['drop-zone', 'dropzone', 'droppable', 'drop-target', 'sortable'];
        const className = element.className.toLowerCase();
        if (dropIndicators.some(indicator => className.includes(indicator))) return true;

        // Strategy 4: Check for data attributes
        if (element.hasAttribute('data-drop') || element.hasAttribute('data-droppable')) return true;

        // Strategy 5: Check for ARIA drop attributes
        if (element.getAttribute('aria-dropeffect') && element.getAttribute('aria-dropeffect') !== 'none') return true;

        return false;
    };

    /**
     * Perfect coordinate-based drop target detection
     * @param {number} x - X coordinate
     * @param {number} y - Y coordinate
     * @returns {Element|null} - Best drop target element or null
     */
    window.cremoteDragDrop.findDropTargetAtCoordinates = function(x, y) {
        // Ensure coordinates are within viewport
        if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) {
            console.log('Coordinates outside viewport:', {x, y, viewport: {width: window.innerWidth, height: window.innerHeight}});
            return null;
        }

        const elements = document.elementsFromPoint(x, y);
        if (!elements || elements.length === 0) {
            console.log('No elements found at coordinates:', {x, y});
            return null;
        }

        // Look for explicit drop targets first
        for (const element of elements) {
            if (this.hasDropEventListener(element)) {
                console.log('Found drop target:', element.tagName, element.id, element.className);
                return element;
            }
        }

        // If no explicit drop target, return the topmost non-body element
        const topElement = elements.find(el => el.tagName !== 'HTML' && el.tagName !== 'BODY');
        console.log('Using topmost element as fallback:', topElement?.tagName, topElement?.id, topElement?.className);
        return topElement || elements[0];
    };

    /**
     * Perfect drag to coordinates with comprehensive event handling
     * @param {string} sourceSelector - CSS selector for source element
     * @param {number} x - Target X coordinate
     * @param {number} y - Target Y coordinate
     * @returns {Promise<object>} - Detailed result object
     */
    window.cremoteDragDrop.dragElementToCoordinates = async function(sourceSelector, x, y) {
        const sourceElement = document.querySelector(sourceSelector);

        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }

        const targetElement = this.findDropTargetAtCoordinates(x, y);
        if (!targetElement) {
            throw new Error('No element found at coordinates (' + x + ', ' + y + ')');
        }

        // Ensure source is draggable
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create persistent DataTransfer
        const dataTransfer = new DataTransfer();
        dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dataTransfer.setData('application/x-cremote-drag', JSON.stringify({
            sourceId: sourceElement.id,
            sourceSelector: sourceSelector,
            targetX: x,
            targetY: y,
            timestamp: Date.now()
        }));
        dataTransfer.effectAllowed = 'all';

        // Step 1: Dragstart
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });

        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            return { success: false, reason: 'Dragstart was cancelled', targetElement: null };
        }

        await new Promise(resolve => setTimeout(resolve, 50));

        // Step 2: Dragenter on target
        const dragEnterEvent = new DragEvent('dragenter', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dataTransfer
        });
        targetElement.dispatchEvent(dragEnterEvent);

        // Step 3: Dragover on target (critical!)
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dataTransfer
        });

        // Force preventDefault to allow drop
        dragOverEvent.preventDefault = function() { this.defaultPrevented = true; };
        targetElement.dispatchEvent(dragOverEvent);

        // Step 4: Drop on target
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dataTransfer
        });

        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Step 5: Dragend on source
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });

        sourceElement.dispatchEvent(dragEndEvent);

        return {
            success: dropResult,
            targetElement: {
                tagName: targetElement.tagName,
                id: targetElement.id,
                className: targetElement.className,
                hasDropListener: this.hasDropEventListener(targetElement)
            }
        };
    };

    /**
     * Perfect smart drag to coordinates with optimal strategy selection
     * @param {string} sourceSelector - CSS selector for source element
     * @param {number} x - Target X coordinate
     * @param {number} y - Target Y coordinate
     * @returns {Promise<object>} - Enhanced result with method info
     */
    window.cremoteDragDrop.smartDragToCoordinates = async function(sourceSelector, x, y) {
        const sourceElement = document.querySelector(sourceSelector);

        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }

        const targetElement = this.findDropTargetAtCoordinates(x, y);
        if (!targetElement) {
            throw new Error('No suitable drop target found at coordinates (' + x + ', ' + y + ')');
        }

        const canReceiveDrops = this.hasDropEventListener(targetElement);

        if (canReceiveDrops && targetElement.id) {
            // Use element-to-element drag for maximum reliability
            const success = await this.dragElementToElement(sourceSelector, '#' + targetElement.id);

            return {
                success: success,
                method: 'element-to-element',
                targetElement: {
                    tagName: targetElement.tagName,
                    id: targetElement.id,
                    className: targetElement.className,
                    hasDropListener: true
                }
            };
        } else {
            // Use coordinate-based drag with perfect event handling
            const result = await this.dragElementToCoordinates(sourceSelector, x, y);
            result.method = 'coordinate-based';
            return result;
        }
    };

    console.log('Perfect Cremote drag and drop helpers loaded successfully');

})();
`

	// Inject the JavaScript helpers
	_, err := page.Eval(jsHelpers)
	if err != nil {
		return fmt.Errorf("failed to inject enhanced drag and drop helpers: %v", err)
	}

	return nil
}

// performDragAndDropToCoordinates performs drag and drop from element to specific coordinates
func (d *Daemon) performDragAndDropToCoordinates(page *rod.Page, sourceSelector string, targetX, targetY int) error {
	// First, try the enhanced HTML5 approach with smart target detection
	err := d.performHTML5DragToCoordinates(page, sourceSelector, targetX, targetY)
	if err == nil {
		d.debugLog("HTML5 coordinate drag completed successfully")
		return nil
	}

	d.debugLog("HTML5 coordinate drag failed (%v), falling back to mouse events", err)

	// Fallback to the original mouse-based approach
	// Find source element
	sourceElement, err := page.Element(sourceSelector)
	if err != nil {
		return fmt.Errorf("failed to find source element %s: %v", sourceSelector, err)
	}

	// Get source element position and size
	sourceBox, err := sourceElement.Shape()
	if err != nil {
		return fmt.Errorf("failed to get source element shape: %v", err)
	}

	// Calculate source center point from the first quad
	if len(sourceBox.Quads) == 0 {
		return fmt.Errorf("source element has no quads")
	}
	sourceQuad := sourceBox.Quads[0]
	sourceX := (sourceQuad[0] + sourceQuad[2] + sourceQuad[4] + sourceQuad[6]) / 4
	sourceY := (sourceQuad[1] + sourceQuad[3] + sourceQuad[5] + sourceQuad[7]) / 4

	return d.performDragAndDropBetweenPoints(page, sourceX, sourceY, float64(targetX), float64(targetY))
}

// performHTML5DragToCoordinates performs HTML5 drag to coordinates with smart target detection
func (d *Daemon) performHTML5DragToCoordinates(page *rod.Page, sourceSelector string, targetX, targetY int) error {
	// First, inject the enhanced helper functions that include coordinate support
	err := d.injectEnhancedDragDropHelpers(page)
	if err != nil {
		return fmt.Errorf("failed to inject enhanced helpers: %v", err)
	}

	// Execute the smart coordinate drag
	jsCode := fmt.Sprintf(`
		(async function() {
			try {
				const result = await window.cremoteDragDrop.smartDragToCoordinates('%s', %d, %d);
				return { success: result.success, method: result.method, error: null, targetInfo: result.targetElement };
			} catch (error) {
				return { success: false, error: error.message, method: 'failed', targetInfo: null };
			}
		})()
	`, sourceSelector, targetX, targetY)

	result, err := page.Eval(jsCode)
	if err != nil {
		return fmt.Errorf("failed to execute HTML5 coordinate drag: %v", err)
	}

	// Parse the result
	resultMap := result.Value.Map()
	if resultMap == nil {
		return fmt.Errorf("invalid result from HTML5 coordinate drag")
	}

	success, exists := resultMap["success"]
	if !exists || !success.Bool() {
		errorMsg := "unknown error"
		if errorVal, exists := resultMap["error"]; exists && errorVal.Str() != "" {
			errorMsg = errorVal.Str()
		}
		return fmt.Errorf("HTML5 coordinate drag failed: %s", errorMsg)
	}

	// Log the method used for debugging
	if method, exists := resultMap["method"]; exists && method.Str() != "" {
		d.debugLog("HTML5 coordinate drag used method: %s", method.Str())
	}

	return nil
}

// performDragAndDropByOffset performs drag and drop from element by relative offset
func (d *Daemon) performDragAndDropByOffset(page *rod.Page, sourceSelector string, offsetX, offsetY int) error {
	// First, calculate the target coordinates
	sourceElement, err := page.Element(sourceSelector)
	if err != nil {
		return fmt.Errorf("failed to find source element %s: %v", sourceSelector, err)
	}

	sourceBox, err := sourceElement.Shape()
	if err != nil {
		return fmt.Errorf("failed to get source element shape: %v", err)
	}

	if len(sourceBox.Quads) == 0 {
		return fmt.Errorf("source element has no quads")
	}
	sourceQuad := sourceBox.Quads[0]
	sourceX := (sourceQuad[0] + sourceQuad[2] + sourceQuad[4] + sourceQuad[6]) / 4
	sourceY := (sourceQuad[1] + sourceQuad[3] + sourceQuad[5] + sourceQuad[7]) / 4

	// Calculate target coordinates
	targetX := int(sourceX + float64(offsetX))
	targetY := int(sourceY + float64(offsetY))

	// Try the enhanced HTML5 approach first (reuse coordinate logic)
	err = d.performHTML5DragToCoordinates(page, sourceSelector, targetX, targetY)
	if err == nil {
		d.debugLog("HTML5 offset drag completed successfully")
		return nil
	}

	d.debugLog("HTML5 offset drag failed (%v), falling back to mouse events", err)

	// Fallback to the original mouse-based approach
	return d.performDragAndDropBetweenPoints(page, sourceX, sourceY, float64(targetX), float64(targetY))
}

// performDragAndDropBetweenPoints performs the actual drag and drop using Chrome DevTools Protocol mouse events
func (d *Daemon) performDragAndDropBetweenPoints(page *rod.Page, sourceX, sourceY, targetX, targetY float64) error {
	d.debugLog("Performing drag and drop from (%.2f, %.2f) to (%.2f, %.2f)", sourceX, sourceY, targetX, targetY)

	// Step 1: Move mouse to source position
	err := proto.InputDispatchMouseEvent{
		Type: proto.InputDispatchMouseEventTypeMouseMoved,
		X:    sourceX,
		Y:    sourceY,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to move mouse to source position: %v", err)
	}

	// Step 2: Mouse down at source position
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMousePressed,
		X:          sourceX,
		Y:          sourceY,
		Button:     proto.InputMouseButtonLeft,
		ClickCount: 1,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to press mouse at source position: %v", err)
	}

	// Step 3: Move mouse to target position (this creates the drag)
	// We'll do this in small steps to simulate realistic dragging
	steps := 10
	for i := 1; i <= steps; i++ {
		progress := float64(i) / float64(steps)
		currentX := sourceX + (targetX-sourceX)*progress
		currentY := sourceY + (targetY-sourceY)*progress

		err = proto.InputDispatchMouseEvent{
			Type:   proto.InputDispatchMouseEventTypeMouseMoved,
			X:      currentX,
			Y:      currentY,
			Button: proto.InputMouseButtonLeft,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to move mouse during drag (step %d): %v", i, err)
		}

		// Small delay between steps to make it more realistic
		time.Sleep(10 * time.Millisecond)
	}

	// Step 4: Mouse up at target position (this completes the drop)
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMouseReleased,
		X:          targetX,
		Y:          targetY,
		Button:     proto.InputMouseButtonLeft,
		ClickCount: 1,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to release mouse at target position: %v", err)
	}

	// Wait a moment for any drag and drop events to be processed
	time.Sleep(100 * time.Millisecond)

	d.debugLog("Successfully completed drag and drop operation")
	return nil
}

// rightClick performs a right-click on an element with timeout handling
func (d *Daemon) rightClick(tabID, selector string, timeout int) error {
	d.debugLog("Right-clicking element: %s", selector)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performRightClick(tabID, selector)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("right-click operation timed out after %d seconds", timeout)
	}
}

// performRightClick performs the actual right-click operation
func (d *Daemon) performRightClick(tabID, selector string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element
	element, err := page.Element(selector)
	if err != nil {
		return fmt.Errorf("failed to find element with selector '%s': %v", selector, err)
	}

	// Get element position
	box, err := element.Shape()
	if err != nil {
		return fmt.Errorf("failed to get element shape: %v", err)
	}

	if len(box.Quads) == 0 {
		return fmt.Errorf("element has no quads")
	}

	// Calculate center point
	quad := box.Quads[0]
	centerX := (quad[0] + quad[2] + quad[4] + quad[6]) / 4
	centerY := (quad[1] + quad[3] + quad[5] + quad[7]) / 4

	// Perform right-click using Chrome DevTools Protocol
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMousePressed,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonRight,
		ClickCount: 1,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to press right mouse button: %v", err)
	}

	// Release right mouse button
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMouseReleased,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonRight,
		ClickCount: 1,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to release right mouse button: %v", err)
	}

	d.debugLog("Successfully right-clicked element")
	return nil
}

// doubleClick performs a double-click on an element with timeout handling
func (d *Daemon) doubleClick(tabID, selector string, timeout int) error {
	d.debugLog("Double-clicking element: %s", selector)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performDoubleClick(tabID, selector)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("double-click operation timed out after %d seconds", timeout)
	}
}

// performDoubleClick performs the actual double-click operation
func (d *Daemon) performDoubleClick(tabID, selector string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element
	element, err := page.Element(selector)
	if err != nil {
		return fmt.Errorf("failed to find element with selector '%s': %v", selector, err)
	}

	// Get element position
	box, err := element.Shape()
	if err != nil {
		return fmt.Errorf("failed to get element shape: %v", err)
	}

	if len(box.Quads) == 0 {
		return fmt.Errorf("element has no quads")
	}

	// Calculate center point
	quad := box.Quads[0]
	centerX := (quad[0] + quad[2] + quad[4] + quad[6]) / 4
	centerY := (quad[1] + quad[3] + quad[5] + quad[7]) / 4

	// Perform double-click using Chrome DevTools Protocol
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMousePressed,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonLeft,
		ClickCount: 2,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to press mouse button for double-click: %v", err)
	}

	// Release mouse button
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMouseReleased,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonLeft,
		ClickCount: 2,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to release mouse button for double-click: %v", err)
	}

	d.debugLog("Successfully double-clicked element")
	return nil
}

// middleClick performs a middle-click on an element with timeout handling
func (d *Daemon) middleClick(tabID, selector string, timeout int) error {
	d.debugLog("Middle-clicking element: %s", selector)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performMiddleClick(tabID, selector)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("middle-click operation timed out after %d seconds", timeout)
	}
}

// performMiddleClick performs the actual middle-click operation
func (d *Daemon) performMiddleClick(tabID, selector string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element
	element, err := page.Element(selector)
	if err != nil {
		return fmt.Errorf("failed to find element with selector '%s': %v", selector, err)
	}

	// Get element position
	box, err := element.Shape()
	if err != nil {
		return fmt.Errorf("failed to get element shape: %v", err)
	}

	if len(box.Quads) == 0 {
		return fmt.Errorf("element has no quads")
	}

	// Calculate center point
	quad := box.Quads[0]
	centerX := (quad[0] + quad[2] + quad[4] + quad[6]) / 4
	centerY := (quad[1] + quad[3] + quad[5] + quad[7]) / 4

	// Perform middle-click using Chrome DevTools Protocol
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMousePressed,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonMiddle,
		ClickCount: 1,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to press middle mouse button: %v", err)
	}

	// Release middle mouse button
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMouseReleased,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonMiddle,
		ClickCount: 1,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to release middle mouse button: %v", err)
	}

	d.debugLog("Successfully middle-clicked element")
	return nil
}

// hover moves the mouse over an element without clicking
func (d *Daemon) hover(tabID, selector string, timeout int) error {
	d.debugLog("Hovering over element: %s", selector)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performHover(tabID, selector)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("hover operation timed out after %d seconds", timeout)
	}
}

// performHover performs the actual hover operation
func (d *Daemon) performHover(tabID, selector string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element
	element, err := page.Element(selector)
	if err != nil {
		return fmt.Errorf("failed to find element with selector '%s': %v", selector, err)
	}

	// Get element position
	box, err := element.Shape()
	if err != nil {
		return fmt.Errorf("failed to get element shape: %v", err)
	}

	if len(box.Quads) == 0 {
		return fmt.Errorf("element has no quads")
	}

	// Calculate center point
	quad := box.Quads[0]
	centerX := (quad[0] + quad[2] + quad[4] + quad[6]) / 4
	centerY := (quad[1] + quad[3] + quad[5] + quad[7]) / 4

	// Move mouse to element center (hover)
	err = proto.InputDispatchMouseEvent{
		Type: proto.InputDispatchMouseEventTypeMouseMoved,
		X:    centerX,
		Y:    centerY,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to move mouse to element: %v", err)
	}

	d.debugLog("Successfully hovered over element")
	return nil
}

// mouseMove moves the mouse to specific coordinates
func (d *Daemon) mouseMove(tabID string, x, y int, timeout int) error {
	d.debugLog("Moving mouse to coordinates: (%d, %d)", x, y)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performMouseMove(tabID, x, y)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("mouse move operation timed out after %d seconds", timeout)
	}
}

// performMouseMove performs the actual mouse move operation
func (d *Daemon) performMouseMove(tabID string, x, y int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Move mouse to coordinates
	err = proto.InputDispatchMouseEvent{
		Type: proto.InputDispatchMouseEventTypeMouseMoved,
		X:    float64(x),
		Y:    float64(y),
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to move mouse to coordinates: %v", err)
	}

	d.debugLog("Successfully moved mouse to coordinates")
	return nil
}

// scrollWheel performs mouse wheel scrolling at specific coordinates
func (d *Daemon) scrollWheel(tabID string, x, y, deltaX, deltaY int, timeout int) error {
	d.debugLog("Scrolling with mouse wheel at (%d, %d) with delta (%d, %d)", x, y, deltaX, deltaY)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performScrollWheel(tabID, x, y, deltaX, deltaY)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("scroll wheel operation timed out after %d seconds", timeout)
	}
}

// performScrollWheel performs the actual mouse wheel scroll operation
func (d *Daemon) performScrollWheel(tabID string, x, y, deltaX, deltaY int) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Perform mouse wheel scroll using Chrome DevTools Protocol
	err = proto.InputDispatchMouseEvent{
		Type:   proto.InputDispatchMouseEventTypeMouseWheel,
		X:      float64(x),
		Y:      float64(y),
		DeltaX: float64(deltaX),
		DeltaY: float64(deltaY),
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to perform mouse wheel scroll: %v", err)
	}

	d.debugLog("Successfully performed mouse wheel scroll")
	return nil
}

// keyCombination sends a key combination (e.g., "Ctrl+C", "Alt+Tab")
func (d *Daemon) keyCombination(tabID, keys string, timeout int) error {
	d.debugLog("Sending key combination: %s", keys)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performKeyCombination(tabID, keys)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("key combination operation timed out after %d seconds", timeout)
	}
}

// performKeyCombination performs the actual key combination operation
func (d *Daemon) performKeyCombination(tabID, keys string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Parse key combination (e.g., "Ctrl+C", "Alt+Tab", "Shift+Enter")
	parts := strings.Split(keys, "+")
	if len(parts) < 2 {
		return fmt.Errorf("invalid key combination format: %s", keys)
	}

	// Map modifier keys
	modifiers := 0
	var mainKey string

	for i, part := range parts {
		part = strings.TrimSpace(part)
		if i == len(parts)-1 {
			// Last part is the main key
			mainKey = part
		} else {
			// Modifier keys
			switch strings.ToLower(part) {
			case "ctrl", "control":
				modifiers |= 2 // ControlLeft
			case "alt":
				modifiers |= 1 // AltLeft
			case "shift":
				modifiers |= 4 // ShiftLeft
			case "meta", "cmd", "command":
				modifiers |= 8 // MetaLeft
			default:
				return fmt.Errorf("unknown modifier key: %s", part)
			}
		}
	}

	// Convert key name to key code
	keyCode, err := d.getKeyCode(mainKey)
	if err != nil {
		return fmt.Errorf("failed to get key code for '%s': %v", mainKey, err)
	}

	// Send key down events for modifiers
	if modifiers&2 != 0 { // Ctrl
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Control",
			Code:      "ControlLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Ctrl key down: %v", err)
		}
	}
	if modifiers&1 != 0 { // Alt
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Alt",
			Code:      "AltLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Alt key down: %v", err)
		}
	}
	if modifiers&4 != 0 { // Shift
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Shift",
			Code:      "ShiftLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Shift key down: %v", err)
		}
	}
	if modifiers&8 != 0 { // Meta
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Meta",
			Code:      "MetaLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Meta key down: %v", err)
		}
	}

	// Send main key down
	err = proto.InputDispatchKeyEvent{
		Type:      proto.InputDispatchKeyEventTypeKeyDown,
		Key:       mainKey,
		Code:      keyCode,
		Modifiers: modifiers,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to send main key down: %v", err)
	}

	// Send main key up
	err = proto.InputDispatchKeyEvent{
		Type:      proto.InputDispatchKeyEventTypeKeyUp,
		Key:       mainKey,
		Code:      keyCode,
		Modifiers: modifiers,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to send main key up: %v", err)
	}

	// Send key up events for modifiers (in reverse order)
	if modifiers&8 != 0 { // Meta
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Meta",
			Code:      "MetaLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Meta key up: %v", err)
		}
	}
	if modifiers&4 != 0 { // Shift
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Shift",
			Code:      "ShiftLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Shift key up: %v", err)
		}
	}
	if modifiers&1 != 0 { // Alt
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Alt",
			Code:      "AltLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Alt key up: %v", err)
		}
	}
	if modifiers&2 != 0 { // Ctrl
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Control",
			Code:      "ControlLeft",
			Modifiers: modifiers,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Ctrl key up: %v", err)
		}
	}

	d.debugLog("Successfully sent key combination")
	return nil
}

// getKeyCode converts a key name to its corresponding key code
func (d *Daemon) getKeyCode(key string) (string, error) {
	// Map common key names to their codes
	keyMap := map[string]string{
		// Letters
		"a": "KeyA", "b": "KeyB", "c": "KeyC", "d": "KeyD", "e": "KeyE", "f": "KeyF",
		"g": "KeyG", "h": "KeyH", "i": "KeyI", "j": "KeyJ", "k": "KeyK", "l": "KeyL",
		"m": "KeyM", "n": "KeyN", "o": "KeyO", "p": "KeyP", "q": "KeyQ", "r": "KeyR",
		"s": "KeyS", "t": "KeyT", "u": "KeyU", "v": "KeyV", "w": "KeyW", "x": "KeyX",
		"y": "KeyY", "z": "KeyZ",

		// Numbers
		"0": "Digit0", "1": "Digit1", "2": "Digit2", "3": "Digit3", "4": "Digit4",
		"5": "Digit5", "6": "Digit6", "7": "Digit7", "8": "Digit8", "9": "Digit9",

		// Function keys
		"F1": "F1", "F2": "F2", "F3": "F3", "F4": "F4", "F5": "F5", "F6": "F6",
		"F7": "F7", "F8": "F8", "F9": "F9", "F10": "F10", "F11": "F11", "F12": "F12",

		// Special keys
		"Enter": "Enter", "Return": "Enter",
		"Escape": "Escape", "Esc": "Escape",
		"Tab":   "Tab",
		"Space": "Space", " ": "Space",
		"Backspace": "Backspace",
		"Delete":    "Delete", "Del": "Delete",
		"Insert": "Insert", "Ins": "Insert",
		"Home":   "Home",
		"End":    "End",
		"PageUp": "PageUp", "PgUp": "PageUp",
		"PageDown": "PageDown", "PgDn": "PageDown",

		// Arrow keys
		"ArrowUp": "ArrowUp", "Up": "ArrowUp",
		"ArrowDown": "ArrowDown", "Down": "ArrowDown",
		"ArrowLeft": "ArrowLeft", "Left": "ArrowLeft",
		"ArrowRight": "ArrowRight", "Right": "ArrowRight",

		// Punctuation
		";": "Semicolon", ":": "Semicolon",
		"=": "Equal", "+": "Equal",
		",": "Comma", "<": "Comma",
		"-": "Minus", "_": "Minus",
		".": "Period", ">": "Period",
		"/": "Slash", "?": "Slash",
		"`": "Backquote", "~": "Backquote",
		"[": "BracketLeft", "{": "BracketLeft",
		"\\": "Backslash", "|": "Backslash",
		"]": "BracketRight", "}": "BracketRight",
		"'": "Quote", "\"": "Quote",
	}

	// Convert to lowercase for lookup
	lowerKey := strings.ToLower(key)
	if code, exists := keyMap[lowerKey]; exists {
		return code, nil
	}

	// If not found in map, try the key as-is (might be a valid code already)
	return key, nil
}

// specialKey sends a special key (e.g., "Enter", "Escape", "Tab", "F1", "ArrowUp")
func (d *Daemon) specialKey(tabID, key string, timeout int) error {
	d.debugLog("Sending special key: %s", key)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performSpecialKey(tabID, key)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("special key operation timed out after %d seconds", timeout)
	}
}

// performSpecialKey performs the actual special key operation
func (d *Daemon) performSpecialKey(tabID, key string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Convert key name to key code
	keyCode, err := d.getKeyCode(key)
	if err != nil {
		return fmt.Errorf("failed to get key code for '%s': %v", key, err)
	}

	// Send key down
	err = proto.InputDispatchKeyEvent{
		Type: proto.InputDispatchKeyEventTypeKeyDown,
		Key:  key,
		Code: keyCode,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to send key down: %v", err)
	}

	// Send key up
	err = proto.InputDispatchKeyEvent{
		Type: proto.InputDispatchKeyEventTypeKeyUp,
		Key:  key,
		Code: keyCode,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to send key up: %v", err)
	}

	d.debugLog("Successfully sent special key")
	return nil
}

// modifierClick performs a click with modifier keys (e.g., Ctrl+click, Shift+click)
func (d *Daemon) modifierClick(tabID, selector, modifiers string, timeout int) error {
	d.debugLog("Performing modifier click on element: %s with modifiers: %s", selector, modifiers)

	// Create context with timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// Execute in goroutine with timeout
	done := make(chan error, 1)
	go func() {
		done <- d.performModifierClick(tabID, selector, modifiers)
	}()

	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		return fmt.Errorf("modifier click operation timed out after %d seconds", timeout)
	}
}

// performModifierClick performs the actual modifier click operation
func (d *Daemon) performModifierClick(tabID, selector, modifiers string) error {
	page, err := d.getTab(tabID)
	if err != nil {
		return err
	}

	// Find the element
	element, err := page.Element(selector)
	if err != nil {
		return fmt.Errorf("failed to find element with selector '%s': %v", selector, err)
	}

	// Get element position
	box, err := element.Shape()
	if err != nil {
		return fmt.Errorf("failed to get element shape: %v", err)
	}

	if len(box.Quads) == 0 {
		return fmt.Errorf("element has no quads")
	}

	// Calculate center point
	quad := box.Quads[0]
	centerX := (quad[0] + quad[2] + quad[4] + quad[6]) / 4
	centerY := (quad[1] + quad[3] + quad[5] + quad[7]) / 4

	// Parse modifiers
	modifierBits := 0
	modifierParts := strings.Split(modifiers, "+")
	for _, mod := range modifierParts {
		mod = strings.TrimSpace(strings.ToLower(mod))
		switch mod {
		case "ctrl", "control":
			modifierBits |= 2 // ControlLeft
		case "alt":
			modifierBits |= 1 // AltLeft
		case "shift":
			modifierBits |= 4 // ShiftLeft
		case "meta", "cmd", "command":
			modifierBits |= 8 // MetaLeft
		default:
			return fmt.Errorf("unknown modifier: %s", mod)
		}
	}

	// Send modifier key down events
	if modifierBits&2 != 0 { // Ctrl
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Control",
			Code:      "ControlLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Ctrl key down: %v", err)
		}
	}
	if modifierBits&1 != 0 { // Alt
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Alt",
			Code:      "AltLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Alt key down: %v", err)
		}
	}
	if modifierBits&4 != 0 { // Shift
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Shift",
			Code:      "ShiftLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Shift key down: %v", err)
		}
	}
	if modifierBits&8 != 0 { // Meta
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyDown,
			Key:       "Meta",
			Code:      "MetaLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Meta key down: %v", err)
		}
	}

	// Perform click with modifiers
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMousePressed,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonLeft,
		ClickCount: 1,
		Modifiers:  modifierBits,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to press mouse button with modifiers: %v", err)
	}

	// Release mouse button
	err = proto.InputDispatchMouseEvent{
		Type:       proto.InputDispatchMouseEventTypeMouseReleased,
		X:          centerX,
		Y:          centerY,
		Button:     proto.InputMouseButtonLeft,
		ClickCount: 1,
		Modifiers:  modifierBits,
	}.Call(page)
	if err != nil {
		return fmt.Errorf("failed to release mouse button with modifiers: %v", err)
	}

	// Send modifier key up events (in reverse order)
	if modifierBits&8 != 0 { // Meta
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Meta",
			Code:      "MetaLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Meta key up: %v", err)
		}
	}
	if modifierBits&4 != 0 { // Shift
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Shift",
			Code:      "ShiftLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Shift key up: %v", err)
		}
	}
	if modifierBits&1 != 0 { // Alt
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Alt",
			Code:      "AltLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Alt key up: %v", err)
		}
	}
	if modifierBits&2 != 0 { // Ctrl
		err = proto.InputDispatchKeyEvent{
			Type:      proto.InputDispatchKeyEventTypeKeyUp,
			Key:       "Control",
			Code:      "ControlLeft",
			Modifiers: modifierBits,
		}.Call(page)
		if err != nil {
			return fmt.Errorf("failed to send Ctrl key up: %v", err)
		}
	}

	d.debugLog("Successfully performed modifier click")
	return nil
}

// Placeholder implementations for remaining methods
// These will be fully implemented in subsequent updates

func (d *Daemon) touchTap(tabID string, x, y int, timeout int) error {
	return fmt.Errorf("touch-tap not yet implemented")
}

func (d *Daemon) touchLongPress(tabID string, x, y, duration int, timeout int) error {
	return fmt.Errorf("touch-long-press not yet implemented")
}

func (d *Daemon) touchSwipe(tabID string, startX, startY, endX, endY int, timeout int) error {
	return fmt.Errorf("touch-swipe not yet implemented")
}

func (d *Daemon) pinchZoom(tabID string, centerX, centerY int, scale float64, timeout int) error {
	return fmt.Errorf("pinch-zoom not yet implemented")
}

func (d *Daemon) scrollElement(tabID, selector string, deltaX, deltaY int, timeout int) error {
	return fmt.Errorf("scroll-element not yet implemented")
}

func (d *Daemon) scrollToCoordinates(tabID string, x, y int, timeout int) error {
	return fmt.Errorf("scroll-to-coordinates not yet implemented")
}

func (d *Daemon) selectText(tabID, selector string, startIndex, endIndex int, timeout int) error {
	return fmt.Errorf("select-text not yet implemented")
}

func (d *Daemon) selectAllText(tabID, selector string, timeout int) error {
	return fmt.Errorf("select-all-text not yet implemented")
}

// AxeResults represents the results from running axe-core accessibility tests
type AxeResults struct {
	Violations   []AxeViolation    `json:"violations"`
	Passes       []AxePass         `json:"passes"`
	Incomplete   []AxeIncomplete   `json:"incomplete"`
	Inapplicable []AxeInapplicable `json:"inapplicable"`
	TestEngine   AxeTestEngine     `json:"testEngine"`
	TestRunner   AxeTestRunner     `json:"testRunner"`
	Timestamp    string            `json:"timestamp"`
	URL          string            `json:"url"`
}

// AxeViolation represents an accessibility violation found by axe-core
type AxeViolation struct {
	ID          string    `json:"id"`
	Impact      string    `json:"impact"`
	Tags        []string  `json:"tags"`
	Description string    `json:"description"`
	Help        string    `json:"help"`
	HelpURL     string    `json:"helpUrl"`
	Nodes       []AxeNode `json:"nodes"`
}

// AxePass represents an accessibility check that passed
type AxePass struct {
	ID          string    `json:"id"`
	Impact      string    `json:"impact"`
	Tags        []string  `json:"tags"`
	Description string    `json:"description"`
	Help        string    `json:"help"`
	HelpURL     string    `json:"helpUrl"`
	Nodes       []AxeNode `json:"nodes"`
}

// AxeIncomplete represents an accessibility check that needs manual review
type AxeIncomplete struct {
	ID          string    `json:"id"`
	Impact      string    `json:"impact"`
	Tags        []string  `json:"tags"`
	Description string    `json:"description"`
	Help        string    `json:"help"`
	HelpURL     string    `json:"helpUrl"`
	Nodes       []AxeNode `json:"nodes"`
}

// AxeInapplicable represents an accessibility check that doesn't apply to this page
type AxeInapplicable struct {
	ID          string   `json:"id"`
	Impact      string   `json:"impact"`
	Tags        []string `json:"tags"`
	Description string   `json:"description"`
	Help        string   `json:"help"`
	HelpURL     string   `json:"helpUrl"`
}

// AxeNode represents a specific DOM node with accessibility issues
type AxeNode struct {
	HTML   string           `json:"html"`
	Impact string           `json:"impact"`
	Target []string         `json:"target"`
	Any    []AxeCheckResult `json:"any"`
	All    []AxeCheckResult `json:"all"`
	None   []AxeCheckResult `json:"none"`
}

// AxeCheckResult represents the result of a specific accessibility check
type AxeCheckResult struct {
	ID      string          `json:"id"`
	Impact  string          `json:"impact"`
	Message string          `json:"message"`
	Data    json.RawMessage `json:"data"` // Can be string or object, use RawMessage
}

// AxeTestEngine represents the axe-core test engine information
type AxeTestEngine struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

// AxeTestRunner represents the test runner information
type AxeTestRunner struct {
	Name string `json:"name"`
}

// injectLibrary injects a JavaScript library from URL or known library name
func (d *Daemon) injectLibrary(tabID string, library string, timeout int) error {
	d.debugLog("Injecting library for tab: %s, library: %s", tabID, library)

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Map of known libraries to their CDN URLs
	knownLibraries := map[string]string{
		"axe":        "https://cdn.jsdelivr.net/npm/axe-core@4.8.0/axe.min.js",
		"axe-core":   "https://cdn.jsdelivr.net/npm/axe-core@4.8.0/axe.min.js",
		"jquery":     "https://code.jquery.com/jquery-3.7.1.min.js",
		"lodash":     "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
		"moment":     "https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js",
		"underscore": "https://cdn.jsdelivr.net/npm/underscore@1.13.6/underscore-min.js",
	}

	// Determine the URL to inject
	var libraryURL string
	if strings.HasPrefix(library, "http://") || strings.HasPrefix(library, "https://") {
		// Direct URL provided
		libraryURL = library
	} else {
		// Check if it's a known library
		if url, ok := knownLibraries[strings.ToLower(library)]; ok {
			libraryURL = url
		} else {
			return fmt.Errorf("unknown library '%s' and not a valid URL", library)
		}
	}

	// JavaScript code to inject the library
	injectCode := fmt.Sprintf(`() => {
		return new Promise((resolve, reject) => {
			// Check if script is already loaded
			const existingScript = document.querySelector('script[src="%s"]');
			if (existingScript) {
				resolve(true);
				return;
			}

			const script = document.createElement('script');
			script.src = '%s';
			script.onload = () => resolve(true);
			script.onerror = () => reject(new Error('Failed to load library from %s'));
			document.head.appendChild(script);
		});
	}`, libraryURL, libraryURL, libraryURL)

	// Execute with timeout
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan error, 1)
		go func() {
			_, err := page.Eval(injectCode)
			done <- err
		}()

		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to inject library: %w", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("library injection timed out after %d seconds", timeout)
		}
	} else {
		_, err = page.Eval(injectCode)
		if err != nil {
			return fmt.Errorf("failed to inject library: %w", err)
		}
	}

	d.debugLog("Successfully injected library: %s", library)
	return nil
}

// injectAxeCore injects the axe-core library into the page
func (d *Daemon) injectAxeCore(tabID string, axeVersion string, timeout int) error {
	d.debugLog("Injecting axe-core library into tab: %s (version: %s)", tabID, axeVersion)

	page, err := d.getTab(tabID)
	if err != nil {
		return fmt.Errorf("failed to get page: %v", err)
	}

	// Default to latest stable version if not specified
	if axeVersion == "" {
		axeVersion = "4.8.0"
	}

	// Check if axe is already loaded
	checkCode := `() => typeof axe !== 'undefined'`
	checkResult, err := page.Eval(checkCode)
	if err == nil && checkResult.Value.Bool() {
		d.debugLog("axe-core already loaded in tab: %s", tabID)
		return nil
	}

	// Inject axe-core from CDN
	injectCode := fmt.Sprintf(`() => {
		return new Promise((resolve, reject) => {
			const script = document.createElement('script');
			script.src = 'https://cdn.jsdelivr.net/npm/axe-core@%s/axe.min.js';
			script.onload = () => resolve(true);
			script.onerror = () => reject(new Error('Failed to load axe-core'));
			document.head.appendChild(script);
		});
	}`, axeVersion)

	// Execute injection with timeout
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan error, 1)
		go func() {
			_, err := page.Eval(injectCode)
			done <- err
		}()

		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("failed to inject axe-core: %w", err)
			}
		case <-ctx.Done():
			return fmt.Errorf("axe-core injection timed out after %d seconds", timeout)
		}
	} else {
		_, err := page.Eval(injectCode)
		if err != nil {
			return fmt.Errorf("failed to inject axe-core: %w", err)
		}
	}

	d.debugLog("Successfully injected axe-core into tab: %s", tabID)
	return nil
}

// runAxeCore runs axe-core accessibility tests on the page
func (d *Daemon) runAxeCore(tabID string, options map[string]interface{}, timeout int) (*AxeResults, error) {
	d.debugLog("Running axe-core tests for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Check if axe is loaded, auto-inject if not
	checkCode := `() => typeof axe !== 'undefined'`
	checkResult, err := page.Eval(checkCode)
	if err != nil || checkResult == nil || !checkResult.Value.Bool() {
		d.debugLog("axe-core not loaded, auto-injecting...")
		if err := d.injectAxeCore(tabID, "", 30); err != nil {
			return nil, fmt.Errorf("failed to auto-inject axe-core: %v", err)
		}
	}

	// Build axe.run() options
	optionsJSON := "{}"
	if options != nil && len(options) > 0 {
		optionsBytes, err := json.Marshal(options)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal options: %w", err)
		}
		optionsJSON = string(optionsBytes)
	}

	// Run axe tests - axe.run() returns a Promise, so we need to await it and stringify
	runCode := fmt.Sprintf(`async () => {
		const results = await axe.run(%s);
		return JSON.stringify(results);
	}`, optionsJSON)

	var jsResult *proto.RuntimeRemoteObject
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			result *proto.RuntimeRemoteObject
			err    error
		}, 1)

		go func() {
			result, err := page.Eval(runCode)
			done <- struct {
				result *proto.RuntimeRemoteObject
				err    error
			}{result, err}
		}()

		select {
		case res := <-done:
			if res.err != nil {
				return nil, fmt.Errorf("failed to run axe-core: %w", res.err)
			}
			jsResult = res.result
		case <-ctx.Done():
			return nil, fmt.Errorf("axe-core execution timed out after %d seconds", timeout)
		}
	} else {
		jsResult, err = page.Eval(runCode)
		if err != nil {
			return nil, fmt.Errorf("failed to run axe-core: %w", err)
		}
	}

	if jsResult == nil {
		return nil, fmt.Errorf("axe-core execution returned nil result")
	}

	// Parse the results
	resultsJSON := jsResult.Value.Str()
	var results AxeResults
	err = json.Unmarshal([]byte(resultsJSON), &results)
	if err != nil {
		return nil, fmt.Errorf("failed to parse axe-core results: %w", err)
	}

	d.debugLog("Successfully ran axe-core tests for tab: %s (found %d violations)", tabID, len(results.Violations))
	return &results, nil
}

// ContrastCheckResult represents the result of contrast checking for text elements
type ContrastCheckResult struct {
	TotalElements int                    `json:"total_elements"`
	PassedAA      int                    `json:"passed_aa"`
	PassedAAA     int                    `json:"passed_aaa"`
	FailedAA      int                    `json:"failed_aa"`
	FailedAAA     int                    `json:"failed_aaa"`
	UnableToCheck int                    `json:"unable_to_check"`
	Elements      []ContrastCheckElement `json:"elements"`
}

// ContrastCheckElement represents a single element's contrast check
type ContrastCheckElement struct {
	Selector        string                 `json:"selector"`
	Text            string                 `json:"text"`
	ForegroundColor string                 `json:"foreground_color"`
	BackgroundColor string                 `json:"background_color"`
	ContrastRatio   float64                `json:"contrast_ratio"`
	FontSize        string                 `json:"font_size"`
	FontWeight      string                 `json:"font_weight"`
	IsLargeText     bool                   `json:"is_large_text"`
	PassesAA        bool                   `json:"passes_aa"`
	PassesAAA       bool                   `json:"passes_aaa"`
	RequiredAA      float64                `json:"required_aa"`
	RequiredAAA     float64                `json:"required_aaa"`
	DetectionMethod string                 `json:"detection_method,omitempty"` // "dom-tree", "stacking-context", "gradient-analysis"
	GradientInfo    map[string]interface{} `json:"gradient_info,omitempty"`    // Additional info for gradient backgrounds
	Error           string                 `json:"error,omitempty"`
}

// checkContrast checks color contrast for text elements on the page
func (d *Daemon) checkContrast(tabID string, selector string, timeout int) (*ContrastCheckResult, error) {
	d.debugLog("Checking contrast for tab: %s, selector: %s", tabID, selector)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Default selector to check all text elements
	if selector == "" {
		selector = "p, h1, h2, h3, h4, h5, h6, a, button, span, div, li, td, th, label, input, textarea"
	}

	// JavaScript code to check contrast for all matching elements
	jsCode := fmt.Sprintf(`() => {
		// Helper function to parse RGB color
		function parseColor(colorStr) {
			const rgb = colorStr.match(/\d+/g);
			if (!rgb || rgb.length < 3) return null;
			return {
				r: parseInt(rgb[0]),
				g: parseInt(rgb[1]),
				b: parseInt(rgb[2]),
				a: rgb.length > 3 ? parseFloat(rgb[3]) : 1
			};
		}

		// Helper function to calculate relative luminance
		function getLuminance(r, g, b) {
			const rsRGB = r / 255;
			const gsRGB = g / 255;
			const bsRGB = b / 255;

			const r2 = rsRGB <= 0.03928 ? rsRGB / 12.92 : Math.pow((rsRGB + 0.055) / 1.055, 2.4);
			const g2 = gsRGB <= 0.03928 ? gsRGB / 12.92 : Math.pow((gsRGB + 0.055) / 1.055, 2.4);
			const b2 = bsRGB <= 0.03928 ? bsRGB / 12.92 : Math.pow((bsRGB + 0.055) / 1.055, 2.4);

			return 0.2126 * r2 + 0.7152 * g2 + 0.0722 * b2;
		}

		// Helper function to calculate contrast ratio
		function getContrastRatio(fg, bg) {
			const l1 = getLuminance(fg.r, fg.g, fg.b);
			const l2 = getLuminance(bg.r, bg.g, bg.b);
			const lighter = Math.max(l1, l2);
			const darker = Math.min(l1, l2);
			return (lighter + 0.05) / (darker + 0.05);
		}

		// Helper function to check if color is transparent
		function isTransparent(colorStr) {
			if (!colorStr || colorStr === 'transparent' || colorStr === 'rgba(0, 0, 0, 0)') {
				return true;
			}
			const parsed = parseColor(colorStr);
			return !parsed || parsed.a === 0;
		}

		// Tier 1: Enhanced DOM tree walking
		function getEffectiveBackgroundDOMTree(element) {
			let current = element;
			while (current && current !== document.documentElement.parentElement) {
				const style = window.getComputedStyle(current);
				const bgColor = style.backgroundColor;

				if (!isTransparent(bgColor)) {
					return bgColor;
				}

				current = current.parentElement;
			}
			return null; // Return null if no background found
		}

		// Tier 2: Stacking context analysis with multi-point sampling
		function getBackgroundFromStackingContext(element) {
			try {
				const rect = element.getBoundingClientRect();

				// Skip if element is not visible
				if (rect.width === 0 || rect.height === 0) {
					return null;
				}

				// Sample multiple points (center, corners) for accuracy
				const samplePoints = [
					{ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 },     // center
					{ x: rect.left + rect.width * 0.25, y: rect.top + rect.height * 0.25 }, // top-left
					{ x: rect.left + rect.width * 0.75, y: rect.top + rect.height * 0.75 }, // bottom-right
					{ x: rect.left + rect.width * 0.75, y: rect.top + rect.height * 0.25 }, // top-right
					{ x: rect.left + rect.width * 0.25, y: rect.top + rect.height * 0.75 }  // bottom-left
				];

				const backgrounds = [];

				for (const point of samplePoints) {
					// Make sure point is within viewport
					if (point.x < 0 || point.y < 0 || point.x > window.innerWidth || point.y > window.innerHeight) {
						continue;
					}

					// Get all elements at this point (in z-index order)
					const elementsAtPoint = document.elementsFromPoint(point.x, point.y);

					// Find the first element with a non-transparent background
					for (const el of elementsAtPoint) {
						if (el === element) continue; // Skip the text element itself

						const bgColor = window.getComputedStyle(el).backgroundColor;
						if (!isTransparent(bgColor)) {
							backgrounds.push(bgColor);
							break;
						}
					}
				}

				// Return the most common background color found
				if (backgrounds.length > 0) {
					// Count occurrences
					const colorCounts = {};
					backgrounds.forEach(color => {
						colorCounts[color] = (colorCounts[color] || 0) + 1;
					});

					// Find most common
					let maxCount = 0;
					let mostCommon = backgrounds[0];
					for (const color in colorCounts) {
						if (colorCounts[color] > maxCount) {
							maxCount = colorCounts[color];
							mostCommon = color;
						}
					}
					return mostCommon;
				}

				return null;
			} catch (e) {
				return null;
			}
		}

		// Tier 3: Gradient analysis - walks up DOM tree to find gradient backgrounds
		// Only returns gradient info if no solid background color is found first
		function analyzeGradientContrast(element, textColor) {
			try {
				// Walk up the DOM tree to find a gradient background
				// Stop if we encounter a solid background color first
				let current = element;
				while (current && current !== document.documentElement.parentElement) {
					const style = window.getComputedStyle(current);

					// Check for solid background color first - if found, gradient doesn't apply
					const bgColor = style.backgroundColor;
					if (!isTransparent(bgColor)) {
						// Solid background found - gradient doesn't apply to this element
						return null;
					}

					// Check if background is a gradient
					const bgImage = style.backgroundImage;
					if (bgImage && bgImage.includes('gradient')) {
						// Extract gradient colors using regex
						const colorRegex = /rgba?\([^)]+\)|#[0-9a-f]{3,6}/gi;
						const matches = bgImage.match(colorRegex) || [];
						// Fix incomplete rgb/rgba matches by adding closing paren
						const colors = matches.map(c => c.includes('(') && !c.includes(')') ? c + ')' : c);

						if (colors.length > 0) {
							// Calculate contrast against each color in the gradient
							const fg = parseColor(textColor);
							if (!fg) {
								current = current.parentElement;
								continue;
							}

							const contrastRatios = [];
							const parsedColors = [];

							for (const colorStr of colors) {
								const bg = parseColor(colorStr);
								if (bg) {
									const ratio = getContrastRatio(fg, bg);
									contrastRatios.push(ratio);
									parsedColors.push(colorStr);
								}
							}

							if (contrastRatios.length > 0) {
								// Return worst-case (minimum) contrast ratio
								const worstCase = Math.min(...contrastRatios);
								const bestCase = Math.max(...contrastRatios);

								return {
									worstCase: worstCase,
									bestCase: bestCase,
									colors: parsedColors,
									isGradient: true
								};
							}
						}
					}

					current = current.parentElement;
				}

				return null;
			} catch (e) {
				return null;
			}
		}

		// Helper function to check if text is large
		function isLargeText(fontSize, fontWeight) {
			const size = parseFloat(fontSize);
			const weight = parseInt(fontWeight) || 400;

			// 18pt (24px) or larger, or 14pt (18.66px) bold or larger
			return size >= 24 || (size >= 18.66 && weight >= 700);
		}

		// Get all matching elements
		const elements = document.querySelectorAll('%s');
		const results = [];

		elements.forEach((element, index) => {
			try {
				// Skip if element has no text content
				const text = element.textContent.trim();
				if (!text || text.length === 0) return;

				// Skip container elements that don't have direct text nodes
				// This prevents checking parent DIVs when we should check the actual text elements (H2, P, etc.)
				const hasDirectTextContent = Array.from(element.childNodes).some(node =>
					node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0
				);

				// For container elements (div, span), only check if they have direct text
				// For semantic text elements (h1-h6, p, a, button, etc.), always check
				const isSemanticTextElement = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'A', 'BUTTON', 'LABEL', 'LI', 'TD', 'TH'].includes(element.tagName);
				const isContainerElement = ['DIV', 'SPAN'].includes(element.tagName);

				if (isContainerElement && !hasDirectTextContent) {
					// Skip this container - its children will be checked separately
					return;
				}

				// Get computed styles
				const style = window.getComputedStyle(element);

				// COMPREHENSIVE VISIBILITY CHECK
				// Skip elements that are not visible (WCAG only applies to visible content)

				// Method 1: Check CSS properties
				const display = style.display;
				const visibility = style.visibility;
				const opacity = parseFloat(style.opacity);

				if (display === 'none' || visibility === 'hidden' || opacity === 0) {
					return;
				}

				// Method 2: Check dimensions (effectively hidden)
				const rect = element.getBoundingClientRect();
				if (rect.width === 0 || rect.height === 0) {
					return;
				}

				// Method 3: Check if element or any parent has display: none
				// This catches cases where parent containers are hidden
				let parent = element.parentElement;
				while (parent && parent !== document.body) {
					const parentStyle = window.getComputedStyle(parent);
					if (parentStyle.display === 'none' ||
						parentStyle.visibility === 'hidden' ||
						parseFloat(parentStyle.opacity) === 0) {
						return;
					}
					parent = parent.parentElement;
				}

				// Method 4: Use offsetParent check (null means element is not rendered)
				// This is the most reliable check for display:none and visibility:hidden
				if (element.offsetParent === null && element.tagName !== 'BODY') {
					// Exception: fixed/sticky positioned elements have null offsetParent but are visible
					const position = style.position;
					if (position !== 'fixed' && position !== 'sticky') {
						return;
					}
				}

				// Get the actual text-bearing element for accurate color detection
				// If the element has child elements with text, use the child's color
				let textElement = element;
				let textStyle = style;

				// Recursively find the element with the most text content
				// This handles cases like <a><span>text</span><br></a>
				function findTextBearingElement(el) {
					// Get all descendant elements (not just direct children)
					const descendants = el.querySelectorAll('*');
					let bestElement = el;
					let maxTextLength = 0;

					// Check the element itself first
					const directText = Array.from(el.childNodes)
						.filter(node => node.nodeType === Node.TEXT_NODE)
						.map(node => node.textContent.trim())
						.join('').length;

					if (directText > maxTextLength) {
						maxTextLength = directText;
						bestElement = el;
					}

					// Check all descendants
					for (const desc of descendants) {
						const descStyle = window.getComputedStyle(desc);
						// Skip invisible elements
						if (descStyle.display === 'none' || descStyle.visibility === 'hidden') {
							continue;
						}

						// Get direct text content (not inherited from children)
						const descText = Array.from(desc.childNodes)
							.filter(node => node.nodeType === Node.TEXT_NODE)
							.map(node => node.textContent.trim())
							.join('').length;

						if (descText > maxTextLength) {
							maxTextLength = descText;
							bestElement = desc;
						}
					}

					return bestElement;
				}

				// Find the element with the most text content
				textElement = findTextBearingElement(element);
				textStyle = window.getComputedStyle(textElement);

				const fgColor = textStyle.color;
				const fontSize = textStyle.fontSize;
				const fontWeight = textStyle.fontWeight;

				// Three-tier background detection with progressive fallback
				let bgColor = null;
				let detectionMethod = null;
				let gradientInfo = null;

				// Tier 1: DOM tree walking for solid colors
				bgColor = getEffectiveBackgroundDOMTree(element);
				if (bgColor) {
					detectionMethod = 'dom-tree';
				}

				// Tier 2: Stacking context analysis (if Tier 1 failed)
				if (!bgColor) {
					bgColor = getBackgroundFromStackingContext(element);
					if (bgColor) {
						detectionMethod = 'stacking-context';
					}
				}

				// Tier 3: Check for gradient backgrounds (only if no solid color found)
				// Gradient analysis will return null if a solid background is found before the gradient
				if (!bgColor) {
					gradientInfo = analyzeGradientContrast(element, fgColor);
					if (gradientInfo) {
						detectionMethod = 'gradient-analysis';
						// Use a representative color from the gradient for the background_color field
						// We'll use the first color for reporting purposes
						bgColor = gradientInfo.colors[0];
					}
				}

				// Final fallback to white if no background detected
				if (!bgColor) {
					bgColor = 'rgb(255, 255, 255)';
					detectionMethod = 'fallback-white';
				}

				// Parse colors
				const fg = parseColor(fgColor);
				const bg = parseColor(bgColor);

				if (!fg || !bg) {
					results.push({
						selector: '%s:nth-of-type(' + (index + 1) + ')',
						text: text.substring(0, 100),
						error: 'Unable to parse colors',
						detection_method: detectionMethod
					});
					return;
				}

				// Calculate contrast ratio (use worst case for gradients)
				let ratio;
				if (gradientInfo) {
					ratio = gradientInfo.worstCase;
				} else {
					ratio = getContrastRatio(fg, bg);
				}

				const large = isLargeText(fontSize, fontWeight);

				// WCAG requirements
				const requiredAA = large ? 3.0 : 4.5;
				const requiredAAA = large ? 4.5 : 7.0;

				const result = {
					selector: '%s:nth-of-type(' + (index + 1) + ')',
					text: text.substring(0, 100),
					foreground_color: fgColor,
					background_color: bgColor,
					contrast_ratio: Math.round(ratio * 100) / 100,
					font_size: fontSize,
					font_weight: fontWeight,
					is_large_text: large,
					passes_aa: ratio >= requiredAA,
					passes_aaa: ratio >= requiredAAA,
					required_aa: requiredAA,
					required_aaa: requiredAAA,
					detection_method: detectionMethod
				};

				// Add gradient info if present
				if (gradientInfo) {
					result.gradient_info = {
						worst_contrast: Math.round(gradientInfo.worstCase * 100) / 100,
						best_contrast: Math.round(gradientInfo.bestCase * 100) / 100,
						colors: gradientInfo.colors,
						is_gradient: true
					};
				}

				results.push(result);
			} catch (e) {
				results.push({
					selector: '%s:nth-of-type(' + (index + 1) + ')',
					text: element.textContent.trim().substring(0, 100),
					error: e.message
				});
			}
		});

		return JSON.stringify(results);
	}`, selector, selector, selector, selector)

	var jsResult *proto.RuntimeRemoteObject
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			result *proto.RuntimeRemoteObject
			err    error
		}, 1)

		go func() {
			result, err := page.Eval(jsCode)
			done <- struct {
				result *proto.RuntimeRemoteObject
				err    error
			}{result, err}
		}()

		select {
		case res := <-done:
			if res.err != nil {
				return nil, fmt.Errorf("failed to check contrast: %w", res.err)
			}
			jsResult = res.result
		case <-ctx.Done():
			return nil, fmt.Errorf("contrast check timed out after %d seconds", timeout)
		}
	} else {
		jsResult, err = page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to check contrast: %w", err)
		}
	}

	if jsResult == nil {
		return nil, fmt.Errorf("contrast check returned nil result")
	}

	// Parse the results
	resultsJSON := jsResult.Value.Str()
	var elements []ContrastCheckElement
	err = json.Unmarshal([]byte(resultsJSON), &elements)
	if err != nil {
		return nil, fmt.Errorf("failed to parse contrast results: %w", err)
	}

	// Calculate summary statistics
	result := &ContrastCheckResult{
		TotalElements: len(elements),
		Elements:      elements,
	}

	for _, elem := range elements {
		if elem.Error != "" {
			result.UnableToCheck++
		} else {
			if elem.PassesAA {
				result.PassedAA++
			} else {
				result.FailedAA++
			}
			if elem.PassesAAA {
				result.PassedAAA++
			} else {
				result.FailedAAA++
			}
		}
	}

	d.debugLog("Successfully checked contrast for tab: %s (checked %d elements)", tabID, len(elements))
	return result, nil
}

// GradientContrastResult represents the result of gradient contrast checking
type GradientContrastResult struct {
	Selector        string  `json:"selector"`
	TextColor       string  `json:"text_color"`
	DarkestBgColor  string  `json:"darkest_bg_color"`
	LightestBgColor string  `json:"lightest_bg_color"`
	WorstContrast   float64 `json:"worst_contrast"`
	BestContrast    float64 `json:"best_contrast"`
	PassesAA        bool    `json:"passes_aa"`
	PassesAAA       bool    `json:"passes_aaa"`
	RequiredAA      float64 `json:"required_aa"`
	RequiredAAA     float64 `json:"required_aaa"`
	IsLargeText     bool    `json:"is_large_text"`
	SamplePoints    int     `json:"sample_points"`
	Error           string  `json:"error,omitempty"`
}

// checkGradientContrast checks color contrast for text on gradient backgrounds using ImageMagick
func (d *Daemon) checkGradientContrast(tabID string, selector string, timeout int) (*GradientContrastResult, error) {
	d.debugLog("Checking gradient contrast for tab: %s, selector: %s", tabID, selector)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Take screenshot of the element
	screenshotPath := fmt.Sprintf("/tmp/gradient-contrast-%d.png", time.Now().UnixNano())
	defer os.Remove(screenshotPath) // Clean up after we're done

	err = d.screenshotElement(tabID, selector, screenshotPath, timeout)
	if err != nil {
		return nil, fmt.Errorf("failed to take element screenshot: %v", err)
	}

	// Get text color and font size from computed styles
	jsCode := fmt.Sprintf(`() => {
		const element = document.querySelector('%s');
		if (!element) {
			return JSON.stringify({error: 'Element not found'});
		}

		const style = window.getComputedStyle(element);
		const fontSize = parseFloat(style.fontSize);
		const fontWeight = parseInt(style.fontWeight) || 400;

		// Determine if this is large text (18pt+ or 14pt+ bold)
		// 1pt = 1.333px, so 18pt = 24px, 14pt = 18.67px
		const isLargeText = fontSize >= 24 || (fontSize >= 18.67 && fontWeight >= 700);

		return JSON.stringify({
			color: style.color,
			fontSize: fontSize,
			fontWeight: fontWeight,
			isLargeText: isLargeText
		});
	}`, selector)

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to get element styles: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("element styles evaluation returned nil result")
	}

	var styleInfo struct {
		Color       string  `json:"color"`
		FontSize    float64 `json:"fontSize"`
		FontWeight  int     `json:"fontWeight"`
		IsLargeText bool    `json:"isLargeText"`
		Error       string  `json:"error,omitempty"`
	}

	err = json.Unmarshal([]byte(jsResult.Value.Str()), &styleInfo)
	if err != nil {
		return nil, fmt.Errorf("failed to parse style info: %v", err)
	}

	if styleInfo.Error != "" {
		return &GradientContrastResult{
			Selector: selector,
			Error:    styleInfo.Error,
		}, nil
	}

	// Parse text color
	textColor, err := d.parseRGBColor(styleInfo.Color)
	if err != nil {
		return &GradientContrastResult{
			Selector: selector,
			Error:    fmt.Sprintf("Failed to parse text color: %v", err),
		}, nil
	}

	// Use ImageMagick to sample colors from the background
	// Resize to 10x10 to get 100 sample points
	cmd := exec.Command("convert", screenshotPath, "-resize", "10x10!", "-depth", "8", "txt:-")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return &GradientContrastResult{
			Selector: selector,
			Error:    fmt.Sprintf("ImageMagick failed: %v - %s", err, string(output)),
		}, nil
	}

	// Parse ImageMagick output to extract colors
	colors, err := d.parseImageMagickColors(string(output))
	if err != nil {
		return &GradientContrastResult{
			Selector: selector,
			Error:    fmt.Sprintf("Failed to parse colors: %v", err),
		}, nil
	}

	if len(colors) == 0 {
		return &GradientContrastResult{
			Selector: selector,
			Error:    "No colors found in image",
		}, nil
	}

	// Calculate contrast ratios against all sampled colors
	var worstContrast, bestContrast float64
	var darkestColor, lightestColor map[string]int

	worstContrast = 21.0 // Maximum possible contrast
	bestContrast = 1.0   // Minimum possible contrast

	for _, bgColor := range colors {
		contrast := d.calculateContrastRatio(textColor, bgColor)

		if contrast < worstContrast {
			worstContrast = contrast
			darkestColor = bgColor
		}
		if contrast > bestContrast {
			bestContrast = contrast
			lightestColor = bgColor
		}
	}

	// Determine required contrast ratios based on text size
	requiredAA := 4.5
	requiredAAA := 7.0
	if styleInfo.IsLargeText {
		requiredAA = 3.0
		requiredAAA = 4.5
	}

	result := &GradientContrastResult{
		Selector:        selector,
		TextColor:       styleInfo.Color,
		DarkestBgColor:  fmt.Sprintf("rgb(%d, %d, %d)", darkestColor["r"], darkestColor["g"], darkestColor["b"]),
		LightestBgColor: fmt.Sprintf("rgb(%d, %d, %d)", lightestColor["r"], lightestColor["g"], lightestColor["b"]),
		WorstContrast:   worstContrast,
		BestContrast:    bestContrast,
		PassesAA:        worstContrast >= requiredAA,
		PassesAAA:       worstContrast >= requiredAAA,
		RequiredAA:      requiredAA,
		RequiredAAA:     requiredAAA,
		IsLargeText:     styleInfo.IsLargeText,
		SamplePoints:    len(colors),
	}

	d.debugLog("Successfully checked gradient contrast for tab: %s, selector: %s (worst: %.2f, best: %.2f)",
		tabID, selector, worstContrast, bestContrast)
	return result, nil
}

// parseRGBColor parses an RGB color string into a map
func (d *Daemon) parseRGBColor(colorStr string) (map[string]int, error) {
	// Match rgb(r, g, b) or rgba(r, g, b, a)
	re := regexp.MustCompile(`rgba?\((\d+),\s*(\d+),\s*(\d+)`)
	matches := re.FindStringSubmatch(colorStr)
	if len(matches) < 4 {
		return nil, fmt.Errorf("invalid color format: %s", colorStr)
	}

	r, _ := strconv.Atoi(matches[1])
	g, _ := strconv.Atoi(matches[2])
	b, _ := strconv.Atoi(matches[3])

	return map[string]int{"r": r, "g": g, "b": b}, nil
}

// parseImageMagickColors parses ImageMagick txt output to extract RGB colors
func (d *Daemon) parseImageMagickColors(output string) ([]map[string]int, error) {
	colors := []map[string]int{}

	// ImageMagick txt format: "0,0: (255,255,255) #FFFFFF srgb(255,255,255)"
	re := regexp.MustCompile(`srgb\((\d+),(\d+),(\d+)\)`)

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		matches := re.FindStringSubmatch(line)
		if len(matches) == 4 {
			r, _ := strconv.Atoi(matches[1])
			g, _ := strconv.Atoi(matches[2])
			b, _ := strconv.Atoi(matches[3])
			colors = append(colors, map[string]int{"r": r, "g": g, "b": b})
		}
	}

	return colors, nil
}

// calculateContrastRatio calculates WCAG contrast ratio between two colors
func (d *Daemon) calculateContrastRatio(color1, color2 map[string]int) float64 {
	l1 := d.getRelativeLuminance(color1["r"], color1["g"], color1["b"])
	l2 := d.getRelativeLuminance(color2["r"], color2["g"], color2["b"])

	lighter := math.Max(l1, l2)
	darker := math.Min(l1, l2)

	return (lighter + 0.05) / (darker + 0.05)
}

// getRelativeLuminance calculates relative luminance for WCAG contrast
func (d *Daemon) getRelativeLuminance(r, g, b int) float64 {
	rsRGB := float64(r) / 255.0
	gsRGB := float64(g) / 255.0
	bsRGB := float64(b) / 255.0

	rLinear := rsRGB
	if rsRGB <= 0.03928 {
		rLinear = rsRGB / 12.92
	} else {
		rLinear = math.Pow((rsRGB+0.055)/1.055, 2.4)
	}

	gLinear := gsRGB
	if gsRGB <= 0.03928 {
		gLinear = gsRGB / 12.92
	} else {
		gLinear = math.Pow((gsRGB+0.055)/1.055, 2.4)
	}

	bLinear := bsRGB
	if bsRGB <= 0.03928 {
		bLinear = bsRGB / 12.92
	} else {
		bLinear = math.Pow((bsRGB+0.055)/1.055, 2.4)
	}

	return 0.2126*rLinear + 0.7152*gLinear + 0.0722*bLinear
}

// MediaValidationResult represents the result of time-based media validation
type MediaValidationResult struct {
	Videos             []MediaElement `json:"videos"`
	Audios             []MediaElement `json:"audios"`
	EmbeddedPlayers    []MediaElement `json:"embedded_players"`
	TranscriptLinks    []string       `json:"transcript_links"`
	TotalViolations    int            `json:"total_violations"`
	CriticalViolations int            `json:"critical_violations"`
	Warnings           int            `json:"warnings"`
}

// MediaElement represents a video or audio element
type MediaElement struct {
	Type              string   `json:"type"` // "video", "audio", "youtube", "vimeo"
	Src               string   `json:"src"`
	HasCaptions       bool     `json:"has_captions"`
	HasDescriptions   bool     `json:"has_descriptions"`
	HasControls       bool     `json:"has_controls"`
	Autoplay          bool     `json:"autoplay"`
	CaptionTracks     []Track  `json:"caption_tracks"`
	DescriptionTracks []Track  `json:"description_tracks"`
	Violations        []string `json:"violations"`
	Warnings          []string `json:"warnings"`
}

// Track represents a text track (captions, descriptions, etc.)
type Track struct {
	Kind       string `json:"kind"`
	Src        string `json:"src"`
	Srclang    string `json:"srclang"`
	Label      string `json:"label"`
	Accessible bool   `json:"accessible"`
}

// validateMedia checks for video/audio captions, descriptions, and transcripts
func (d *Daemon) validateMedia(tabID string, timeout int) (*MediaValidationResult, error) {
	d.debugLog("Validating media for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// JavaScript code to inventory all media elements
	jsCode := `() => {
		const result = {
			videos: [],
			audios: [],
			embeddedPlayers: [],
			transcriptLinks: []
		};

		// Find all video elements
		document.querySelectorAll('video').forEach(video => {
			const videoData = {
				type: 'video',
				src: video.src || video.currentSrc || 'inline',
				hasCaptions: false,
				hasDescriptions: false,
				hasControls: video.hasAttribute('controls'),
				autoplay: video.hasAttribute('autoplay'),
				captionTracks: [],
				descriptionTracks: []
			};

			// Check for text tracks
			video.querySelectorAll('track').forEach(track => {
				const trackData = {
					kind: track.kind,
					src: track.src,
					srclang: track.srclang || '',
					label: track.label || ''
				};

				if (track.kind === 'captions' || track.kind === 'subtitles') {
					videoData.hasCaptions = true;
					videoData.captionTracks.push(trackData);
				} else if (track.kind === 'descriptions') {
					videoData.hasDescriptions = true;
					videoData.descriptionTracks.push(trackData);
				}
			});

			result.videos.push(videoData);
		});

		// Find all audio elements
		document.querySelectorAll('audio').forEach(audio => {
			const audioData = {
				type: 'audio',
				src: audio.src || audio.currentSrc || 'inline',
				hasControls: audio.hasAttribute('controls'),
				autoplay: audio.hasAttribute('autoplay')
			};

			result.audios.push(audioData);
		});

		// Find embedded players (YouTube, Vimeo)
		document.querySelectorAll('iframe[src*="youtube"], iframe[src*="vimeo"]').forEach(iframe => {
			const playerData = {
				type: iframe.src.includes('youtube') ? 'youtube' : 'vimeo',
				src: iframe.src
			};

			result.embeddedPlayers.push(playerData);
		});

		// Find transcript links
		// Note: :contains() is not a valid CSS selector in querySelectorAll
		// We need to check all links and filter by text content
		const transcriptPatterns = ['transcript', 'captions', 'subtitles'];
		document.querySelectorAll('a').forEach(link => {
			const text = link.textContent.toLowerCase();
			const href = link.href.toLowerCase();
			if (transcriptPatterns.some(pattern => text.includes(pattern) || href.includes(pattern))) {
				if (!result.transcriptLinks.includes(link.href)) {
					result.transcriptLinks.push(link.href);
				}
			}
		});

		return JSON.stringify(result);
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to execute media validation: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("media validation returned nil result")
	}

	// Parse the JavaScript result
	var mediaData struct {
		Videos          []MediaElement `json:"videos"`
		Audios          []MediaElement `json:"audios"`
		EmbeddedPlayers []MediaElement `json:"embedded_players"`
		TranscriptLinks []string       `json:"transcript_links"`
	}

	err = json.Unmarshal([]byte(jsResult.Value.Str()), &mediaData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse media data: %v", err)
	}

	// Validate each video element
	totalViolations := 0
	criticalViolations := 0
	warnings := 0

	for i := range mediaData.Videos {
		video := &mediaData.Videos[i]

		// Check for captions (WCAG 1.2.2 - Level A - CRITICAL)
		if !video.HasCaptions {
			video.Violations = append(video.Violations, "CRITICAL: Missing captions (WCAG 1.2.2 Level A)")
			criticalViolations++
			totalViolations++
		}

		// Check for audio descriptions (WCAG 1.2.5 - Level AA)
		if !video.HasDescriptions {
			video.Warnings = append(video.Warnings, "WARNING: Missing audio descriptions (WCAG 1.2.5 Level AA)")
			warnings++
		}

		// Check for controls
		if !video.HasControls {
			video.Warnings = append(video.Warnings, "WARNING: No controls attribute - users cannot pause/adjust")
			warnings++
		}

		// Check for autoplay (WCAG 1.4.2 - Level A)
		if video.Autoplay {
			video.Warnings = append(video.Warnings, "WARNING: Video autoplays - may violate WCAG 1.4.2 if >3 seconds")
			warnings++
		}

		// Validate caption track accessibility
		for j := range video.CaptionTracks {
			track := &video.CaptionTracks[j]
			accessible, err := d.checkTrackAccessibility(tabID, track.Src, timeout)
			track.Accessible = accessible
			if err != nil || !accessible {
				video.Violations = append(video.Violations, fmt.Sprintf("Caption file not accessible: %s", track.Src))
				totalViolations++
			}
		}

		// Validate description track accessibility
		for j := range video.DescriptionTracks {
			track := &video.DescriptionTracks[j]
			accessible, err := d.checkTrackAccessibility(tabID, track.Src, timeout)
			track.Accessible = accessible
			if err != nil || !accessible {
				video.Warnings = append(video.Warnings, fmt.Sprintf("Description file not accessible: %s", track.Src))
				warnings++
			}
		}
	}

	// Validate audio elements
	for i := range mediaData.Audios {
		audio := &mediaData.Audios[i]

		// Check for controls
		if !audio.HasControls {
			audio.Warnings = append(audio.Warnings, "WARNING: No controls attribute - users cannot pause/adjust")
			warnings++
		}

		// Check for autoplay (WCAG 1.4.2 - Level A)
		if audio.Autoplay {
			audio.Warnings = append(audio.Warnings, "WARNING: Audio autoplays - may violate WCAG 1.4.2 if >3 seconds")
			warnings++
		}
	}

	result := &MediaValidationResult{
		Videos:             mediaData.Videos,
		Audios:             mediaData.Audios,
		EmbeddedPlayers:    mediaData.EmbeddedPlayers,
		TranscriptLinks:    mediaData.TranscriptLinks,
		TotalViolations:    totalViolations,
		CriticalViolations: criticalViolations,
		Warnings:           warnings,
	}

	d.debugLog("Successfully validated media for tab: %s (videos: %d, audios: %d, violations: %d)",
		tabID, len(mediaData.Videos), len(mediaData.Audios), totalViolations)
	return result, nil
}

// checkTrackAccessibility checks if a track file is accessible
func (d *Daemon) checkTrackAccessibility(tabID, trackSrc string, timeout int) (bool, error) {
	if trackSrc == "" {
		return false, fmt.Errorf("empty track source")
	}

	// Use JavaScript to fetch the track file
	jsCode := fmt.Sprintf(`async () => {
		try {
			const response = await fetch('%s');
			return response.ok;
		} catch (error) {
			return false;
		}
	}`, trackSrc)

	page, err := d.getTab(tabID)
	if err != nil {
		return false, err
	}

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return false, err
	}
	if jsResult == nil {
		return false, fmt.Errorf("track validation returned nil result")
	}

	return jsResult.Value.Bool(), nil
}

// HoverFocusTestResult represents the result of hover/focus content testing
type HoverFocusTestResult struct {
	TotalElements      int                 `json:"total_elements"`
	ElementsWithIssues int                 `json:"elements_with_issues"`
	PassedElements     int                 `json:"passed_elements"`
	Issues             []HoverFocusIssue   `json:"issues"`
	TestedElements     []HoverFocusElement `json:"tested_elements"`
}

// HoverFocusElement represents an element that shows content on hover/focus
type HoverFocusElement struct {
	Selector    string   `json:"selector"`
	Type        string   `json:"type"` // "tooltip", "dropdown", "popover", "custom"
	Dismissible bool     `json:"dismissible"`
	Hoverable   bool     `json:"hoverable"`
	Persistent  bool     `json:"persistent"`
	PassesWCAG  bool     `json:"passes_wcag"`
	Violations  []string `json:"violations"`
}

// HoverFocusIssue represents a specific issue with hover/focus content
type HoverFocusIssue struct {
	Selector    string `json:"selector"`
	Type        string `json:"type"`     // "not_dismissible", "not_hoverable", "not_persistent"
	Severity    string `json:"severity"` // "critical", "serious", "moderate"
	Description string `json:"description"`
	WCAG        string `json:"wcag"` // "1.4.13"
}

// testHoverFocusContent tests WCAG 1.4.13 compliance for content on hover or focus
func (d *Daemon) testHoverFocusContent(tabID string, timeout int) (*HoverFocusTestResult, error) {
	d.debugLog("Testing hover/focus content for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// JavaScript code to find elements that show content on hover/focus
	jsCode := `() => {
		const result = {
			elements: []
		};

		// Common selectors for elements that show content on hover/focus
		const selectors = [
			'[title]',                    // Elements with tooltips
			'[aria-describedby]',         // Elements with descriptions
			'[data-tooltip]',             // Custom tooltip attributes
			'.tooltip-trigger',           // Common tooltip classes
			'.has-tooltip',
			'[role="tooltip"]',
			'button[aria-haspopup]',      // Buttons with popups
			'a[aria-haspopup]',           // Links with popups
			'[aria-expanded]',            // Expandable elements
			'.dropdown-toggle',           // Dropdown triggers
			'.popover-trigger'            // Popover triggers
		];

		// Find all potential hover/focus elements
		const foundElements = new Set();
		selectors.forEach(selector => {
			try {
				document.querySelectorAll(selector).forEach(el => {
					if (el.offsetParent !== null) { // Only visible elements
						foundElements.add(el);
					}
				});
			} catch (e) {
				// Ignore invalid selectors
			}
		});

		// Test each element
		foundElements.forEach((element, index) => {
			const elementData = {
				selector: '',
				type: 'custom',
				hasTitle: element.hasAttribute('title'),
				hasAriaDescribedby: element.hasAttribute('aria-describedby'),
				hasAriaHaspopup: element.hasAttribute('aria-haspopup'),
				hasAriaExpanded: element.hasAttribute('aria-expanded'),
				role: element.getAttribute('role') || '',
				tagName: element.tagName.toLowerCase()
			};

			// Generate a selector
			if (element.id) {
				elementData.selector = '#' + element.id;
			} else if (element.className) {
				const classes = element.className.split(' ').filter(c => c).slice(0, 2).join('.');
				elementData.selector = element.tagName.toLowerCase() + '.' + classes;
			} else {
				elementData.selector = element.tagName.toLowerCase() + ':nth-of-type(' + (index + 1) + ')';
			}

			// Determine type
			if (element.hasAttribute('title')) {
				elementData.type = 'tooltip';
			} else if (element.classList.contains('dropdown-toggle') || element.hasAttribute('aria-haspopup')) {
				elementData.type = 'dropdown';
			} else if (element.classList.contains('popover-trigger')) {
				elementData.type = 'popover';
			}

			result.elements.push(elementData);
		});

		return JSON.stringify(result);
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to find hover/focus elements: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("hover/focus element search returned nil result")
	}

	// Parse the JavaScript result
	var elementsData struct {
		Elements []struct {
			Selector           string `json:"selector"`
			Type               string `json:"type"`
			HasTitle           bool   `json:"hasTitle"`
			HasAriaDescribedby bool   `json:"hasAriaDescribedby"`
			HasAriaHaspopup    bool   `json:"hasAriaHaspopup"`
			HasAriaExpanded    bool   `json:"hasAriaExpanded"`
			Role               string `json:"role"`
			TagName            string `json:"tagName"`
		} `json:"elements"`
	}

	err = json.Unmarshal([]byte(jsResult.Value.Str()), &elementsData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse elements data: %v", err)
	}

	result := &HoverFocusTestResult{
		TotalElements:  len(elementsData.Elements),
		TestedElements: make([]HoverFocusElement, 0),
		Issues:         make([]HoverFocusIssue, 0),
	}

	// Test each element for WCAG 1.4.13 compliance
	for _, elem := range elementsData.Elements {
		testedElement := HoverFocusElement{
			Selector:    elem.Selector,
			Type:        elem.Type,
			Dismissible: true, // Assume true unless proven false
			Hoverable:   true, // Assume true unless proven false
			Persistent:  true, // Assume true unless proven false
			PassesWCAG:  true,
			Violations:  make([]string, 0),
		}

		// For tooltips (title attribute), check if they're dismissible
		if elem.HasTitle {
			// Native title tooltips are NOT dismissible with Escape key
			testedElement.Dismissible = false
			testedElement.PassesWCAG = false
			testedElement.Violations = append(testedElement.Violations,
				"Native title attribute tooltip is not dismissible with Escape key (WCAG 1.4.13)")

			result.Issues = append(result.Issues, HoverFocusIssue{
				Selector:    elem.Selector,
				Type:        "not_dismissible",
				Severity:    "serious",
				Description: "Native title attribute creates non-dismissible tooltip",
				WCAG:        "1.4.13",
			})
		}

		// For elements with aria-describedby, check if the description is accessible
		if elem.HasAriaDescribedby {
			// These are usually properly implemented, but we flag for manual review
			testedElement.Violations = append(testedElement.Violations,
				"Manual review required: Verify aria-describedby content is dismissible, hoverable, and persistent")
		}

		// For dropdowns and popovers, we can't fully test without interaction
		// Flag for manual review
		if elem.HasAriaHaspopup || elem.HasAriaExpanded {
			testedElement.Violations = append(testedElement.Violations,
				"Manual review required: Test dropdown/popover for dismissibility, hoverability, and persistence")
		}

		if !testedElement.PassesWCAG {
			result.ElementsWithIssues++
		} else {
			result.PassedElements++
		}

		result.TestedElements = append(result.TestedElements, testedElement)
	}

	d.debugLog("Successfully tested hover/focus content for tab: %s (elements: %d, issues: %d)",
		tabID, result.TotalElements, result.ElementsWithIssues)
	return result, nil
}

// TextInImagesResult represents the result of text-in-images detection
type TextInImagesResult struct {
	TotalImages       int                 `json:"total_images"`
	ImagesWithText    int                 `json:"images_with_text"`
	ImagesWithoutText int                 `json:"images_without_text"`
	Violations        int                 `json:"violations"`
	Warnings          int                 `json:"warnings"`
	Images            []ImageTextAnalysis `json:"images"`
}

// ImageTextAnalysis represents OCR analysis of a single image
type ImageTextAnalysis struct {
	Src            string  `json:"src"`
	Alt            string  `json:"alt"`
	HasAlt         bool    `json:"has_alt"`
	DetectedText   string  `json:"detected_text"`
	TextLength     int     `json:"text_length"`
	Confidence     float64 `json:"confidence"`
	IsViolation    bool    `json:"is_violation"`
	ViolationType  string  `json:"violation_type"` // "missing_alt", "insufficient_alt", "decorative_with_text"
	Recommendation string  `json:"recommendation"`
}

// detectTextInImages uses Tesseract OCR to detect text in images
func (d *Daemon) detectTextInImages(tabID string, timeout int) (*TextInImagesResult, error) {
	d.debugLog("Detecting text in images for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// JavaScript code to find all images
	jsCode := `() => {
		const result = {
			images: []
		};

		// Find all img elements
		document.querySelectorAll('img').forEach((img, index) => {
			// Only process visible images
			if (img.offsetParent !== null && img.complete && img.naturalWidth > 0) {
				const imageData = {
					src: img.src || img.currentSrc || '',
					alt: img.alt || '',
					hasAlt: img.hasAttribute('alt'),
					width: img.naturalWidth,
					height: img.naturalHeight,
					index: index
				};

				// Skip very small images (likely icons/decorative)
				if (imageData.width >= 50 && imageData.height >= 50) {
					result.images.push(imageData);
				}
			}
		});

		return JSON.stringify(result);
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to find images: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("image search returned nil result")
	}

	// Parse the JavaScript result
	var imagesData struct {
		Images []struct {
			Src    string `json:"src"`
			Alt    string `json:"alt"`
			HasAlt bool   `json:"hasAlt"`
			Width  int    `json:"width"`
			Height int    `json:"height"`
			Index  int    `json:"index"`
		} `json:"images"`
	}

	err = json.Unmarshal([]byte(jsResult.Value.Str()), &imagesData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse images data: %v", err)
	}

	result := &TextInImagesResult{
		TotalImages: len(imagesData.Images),
		Images:      make([]ImageTextAnalysis, 0),
	}

	// Process each image with OCR
	for _, img := range imagesData.Images {
		analysis := ImageTextAnalysis{
			Src:    img.Src,
			Alt:    img.Alt,
			HasAlt: img.HasAlt,
		}

		// Download image and run OCR
		detectedText, confidence, err := d.runOCROnImage(img.Src, timeout)
		if err != nil {
			d.debugLog("Failed to run OCR on image %s: %v", img.Src, err)
			// Continue with other images
			continue
		}

		analysis.DetectedText = detectedText
		analysis.TextLength = len(detectedText)
		analysis.Confidence = confidence

		// Determine if this is a violation
		if analysis.TextLength > 10 { // Significant text detected
			result.ImagesWithText++

			if !analysis.HasAlt || analysis.Alt == "" {
				// CRITICAL: Image has text but no alt text
				analysis.IsViolation = true
				analysis.ViolationType = "missing_alt"
				analysis.Recommendation = "Add alt text that includes the text content: \"" + detectedText + "\""
				result.Violations++
			} else if len(analysis.Alt) < analysis.TextLength/2 {
				// WARNING: Alt text seems insufficient for amount of text
				analysis.IsViolation = true
				analysis.ViolationType = "insufficient_alt"
				analysis.Recommendation = "Alt text may be insufficient. Detected text: \"" + detectedText + "\""
				result.Warnings++
			} else {
				// Alt text exists and seems adequate
				analysis.IsViolation = false
				analysis.Recommendation = "Alt text present - verify it includes the text content"
			}
		} else {
			result.ImagesWithoutText++
		}

		result.Images = append(result.Images, analysis)
	}

	d.debugLog("Successfully detected text in images for tab: %s (total: %d, with text: %d, violations: %d)",
		tabID, result.TotalImages, result.ImagesWithText, result.Violations)
	return result, nil
}

// runOCROnImage downloads an image and runs Tesseract OCR on it
func (d *Daemon) runOCROnImage(imageSrc string, timeout int) (string, float64, error) {
	// Create temporary file for image
	tmpFile, err := os.CreateTemp("", "ocr-image-*.png")
	if err != nil {
		return "", 0, fmt.Errorf("failed to create temp file: %v", err)
	}
	defer os.Remove(tmpFile.Name())
	tmpFile.Close()

	// Download image (simple approach - could be enhanced)
	// For now, we'll use a simple HTTP GET
	// In production, this should handle data URLs, relative URLs, etc.

	// Skip data URLs for now
	if strings.HasPrefix(imageSrc, "data:") {
		return "", 0, fmt.Errorf("data URLs not supported yet")
	}

	// Use curl to download (more reliable than Go's http.Get for various scenarios)
	downloadCmd := exec.Command("curl", "-s", "-L", "-o", tmpFile.Name(), imageSrc)
	downloadCmd.Stdout = nil
	downloadCmd.Stderr = nil

	err = downloadCmd.Run()
	if err != nil {
		return "", 0, fmt.Errorf("failed to download image: %v", err)
	}

	// Run Tesseract OCR
	outputFile := tmpFile.Name() + "-output"
	defer os.Remove(outputFile + ".txt")

	tesseractCmd := exec.Command("tesseract", tmpFile.Name(), outputFile, "--psm", "6")
	tesseractCmd.Stdout = nil
	tesseractCmd.Stderr = nil

	err = tesseractCmd.Run()
	if err != nil {
		return "", 0, fmt.Errorf("failed to run tesseract: %v", err)
	}

	// Read OCR output
	ocrOutput, err := os.ReadFile(outputFile + ".txt")
	if err != nil {
		return "", 0, fmt.Errorf("failed to read OCR output: %v", err)
	}

	// Clean up the text
	text := strings.TrimSpace(string(ocrOutput))

	// Calculate confidence (simplified - Tesseract can provide detailed confidence)
	// For now, we'll use a simple heuristic based on text length and character variety
	confidence := 0.8 // Default confidence
	if len(text) > 0 {
		confidence = 0.9
	}

	return text, confidence, nil
}

// CrossPageConsistencyResult represents the result of cross-page consistency checking
type CrossPageConsistencyResult struct {
	PagesAnalyzed     int                       `json:"pages_analyzed"`
	ConsistencyIssues int                       `json:"consistency_issues"`
	NavigationIssues  int                       `json:"navigation_issues"`
	StructureIssues   int                       `json:"structure_issues"`
	Pages             []PageConsistencyAnalysis `json:"pages"`
	CommonNavigation  []string                  `json:"common_navigation"`
	InconsistentPages []string                  `json:"inconsistent_pages"`
}

// PageConsistencyAnalysis represents consistency analysis of a single page
type PageConsistencyAnalysis struct {
	URL                 string   `json:"url"`
	Title               string   `json:"title"`
	HasHeader           bool     `json:"has_header"`
	HasFooter           bool     `json:"has_footer"`
	HasNavigation       bool     `json:"has_navigation"`
	NavigationLinks     []string `json:"navigation_links"`
	MainLandmarks       int      `json:"main_landmarks"`
	HeaderLandmarks     int      `json:"header_landmarks"`
	FooterLandmarks     int      `json:"footer_landmarks"`
	NavigationLandmarks int      `json:"navigation_landmarks"`
	Issues              []string `json:"issues"`
}

// checkCrossPageConsistency analyzes multiple pages for consistency
func (d *Daemon) checkCrossPageConsistency(tabID string, urls []string, timeout int) (*CrossPageConsistencyResult, error) {
	d.debugLog("Checking cross-page consistency for %d URLs", len(urls))

	if len(urls) == 0 {
		return nil, fmt.Errorf("no URLs provided for consistency check")
	}

	result := &CrossPageConsistencyResult{
		Pages:             make([]PageConsistencyAnalysis, 0),
		CommonNavigation:  make([]string, 0),
		InconsistentPages: make([]string, 0),
	}

	// Analyze each page
	for _, url := range urls {
		pageAnalysis, err := d.analyzePageConsistency(tabID, url, timeout)
		if err != nil {
			d.debugLog("Failed to analyze page %s: %v", url, err)
			continue
		}

		result.Pages = append(result.Pages, *pageAnalysis)
		result.PagesAnalyzed++
	}

	// Find common navigation elements
	if len(result.Pages) > 1 {
		navMap := make(map[string]int)
		for _, page := range result.Pages {
			for _, link := range page.NavigationLinks {
				navMap[link]++
			}
		}

		// Links that appear on all pages are "common navigation"
		for link, count := range navMap {
			if count == len(result.Pages) {
				result.CommonNavigation = append(result.CommonNavigation, link)
			}
		}

		// Check for inconsistencies
		for _, page := range result.Pages {
			issues := 0

			// Check if page has all common navigation
			for _, commonLink := range result.CommonNavigation {
				found := false
				for _, pageLink := range page.NavigationLinks {
					if pageLink == commonLink {
						found = true
						break
					}
				}
				if !found {
					page.Issues = append(page.Issues, "Missing common navigation link: "+commonLink)
					issues++
				}
			}

			// Check for multiple main landmarks (should be 1)
			if page.MainLandmarks != 1 {
				page.Issues = append(page.Issues, fmt.Sprintf("Should have exactly 1 main landmark, found %d", page.MainLandmarks))
				issues++
				result.StructureIssues++
			}

			// Check for header/footer presence
			if !page.HasHeader {
				page.Issues = append(page.Issues, "Missing header landmark")
				issues++
				result.StructureIssues++
			}

			if !page.HasFooter {
				page.Issues = append(page.Issues, "Missing footer landmark")
				issues++
				result.StructureIssues++
			}

			if !page.HasNavigation {
				page.Issues = append(page.Issues, "Missing navigation landmark")
				issues++
				result.NavigationIssues++
			}

			if issues > 0 {
				result.InconsistentPages = append(result.InconsistentPages, page.URL)
				result.ConsistencyIssues += issues
			}
		}
	}

	d.debugLog("Successfully checked cross-page consistency: %d pages, %d issues",
		result.PagesAnalyzed, result.ConsistencyIssues)
	return result, nil
}

// analyzePageConsistency analyzes a single page for consistency elements
func (d *Daemon) analyzePageConsistency(tabID, url string, timeout int) (*PageConsistencyAnalysis, error) {
	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Navigate to URL
	err = page.Navigate(url)
	if err != nil {
		return nil, fmt.Errorf("failed to navigate to %s: %v", url, err)
	}

	// Wait for page to load
	err = page.WaitLoad()
	if err != nil {
		return nil, fmt.Errorf("failed to wait for page load: %v", err)
	}

	// JavaScript code to analyze page structure
	jsCode := `() => {
		const result = {
			url: window.location.href,
			title: document.title,
			hasHeader: false,
			hasFooter: false,
			hasNavigation: false,
			navigationLinks: [],
			mainLandmarks: 0,
			headerLandmarks: 0,
			footerLandmarks: 0,
			navigationLandmarks: 0
		};

		// Count landmarks
		result.mainLandmarks = document.querySelectorAll('main, [role="main"]').length;
		result.headerLandmarks = document.querySelectorAll('header, [role="banner"]').length;
		result.footerLandmarks = document.querySelectorAll('footer, [role="contentinfo"]').length;
		result.navigationLandmarks = document.querySelectorAll('nav, [role="navigation"]').length;

		result.hasHeader = result.headerLandmarks > 0;
		result.hasFooter = result.footerLandmarks > 0;
		result.hasNavigation = result.navigationLandmarks > 0;

		// Extract navigation links
		document.querySelectorAll('nav a, [role="navigation"] a').forEach(link => {
			if (link.href && !link.href.startsWith('javascript:')) {
				result.navigationLinks.push(link.textContent.trim());
			}
		});

		return JSON.stringify(result);
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to analyze page structure: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("page structure analysis returned nil result")
	}

	// Parse the JavaScript result
	var analysis PageConsistencyAnalysis
	err = json.Unmarshal([]byte(jsResult.Value.Str()), &analysis)
	if err != nil {
		return nil, fmt.Errorf("failed to parse page analysis: %v", err)
	}

	analysis.Issues = make([]string, 0)

	return &analysis, nil
}

// AnimationFlashResult represents the result of animation/flash detection
type AnimationFlashResult struct {
	TotalAnimations    int                     `json:"total_animations"`
	FlashingContent    int                     `json:"flashing_content"`
	RapidAnimations    int                     `json:"rapid_animations"`
	AutoplayAnimations int                     `json:"autoplay_animations"`
	Violations         int                     `json:"violations"`
	Warnings           int                     `json:"warnings"`
	Elements           []AnimationFlashElement `json:"elements"`
}

// AnimationFlashElement represents an animated or flashing element
type AnimationFlashElement struct {
	TagName        string  `json:"tag_name"`
	Selector       string  `json:"selector"`
	AnimationType  string  `json:"animation_type"` // "css", "gif", "video", "canvas", "svg"
	FlashRate      float64 `json:"flash_rate"`     // Flashes per second
	Duration       float64 `json:"duration"`       // Animation duration in seconds
	IsAutoplay     bool    `json:"is_autoplay"`
	HasControls    bool    `json:"has_controls"`
	CanPause       bool    `json:"can_pause"`
	IsViolation    bool    `json:"is_violation"`
	ViolationType  string  `json:"violation_type"`
	Recommendation string  `json:"recommendation"`
}

// detectAnimationFlash detects animations and flashing content
func (d *Daemon) detectAnimationFlash(tabID string, timeout int) (*AnimationFlashResult, error) {
	d.debugLog("Detecting animation/flash for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// JavaScript code to detect animations and flashing content
	jsCode := `() => {
		const result = {
			elements: []
		};

		// Helper function to get element selector
		function getSelector(element) {
			if (element.id) return '#' + element.id;
			if (element.className && typeof element.className === 'string') {
				const classes = element.className.trim().split(/\s+/).slice(0, 2).join('.');
				if (classes) return element.tagName.toLowerCase() + '.' + classes;
			}
			return element.tagName.toLowerCase();
		}

		// 1. Detect CSS animations
		document.querySelectorAll('*').forEach(element => {
			const styles = window.getComputedStyle(element);
			const animationName = styles.animationName;
			const animationDuration = parseFloat(styles.animationDuration);
			const animationIterationCount = styles.animationIterationCount;

			if (animationName && animationName !== 'none' && animationDuration > 0) {
				const isInfinite = animationIterationCount === 'infinite';
				result.elements.push({
					tagName: element.tagName.toLowerCase(),
					selector: getSelector(element),
					animationType: 'css',
					duration: animationDuration,
					isAutoplay: true,
					hasControls: false,
					canPause: false,
					isInfinite: isInfinite
				});
			}
		});

		// 2. Detect GIF images
		document.querySelectorAll('img').forEach(img => {
			if (img.src && (img.src.toLowerCase().endsWith('.gif') || img.src.includes('.gif?'))) {
				result.elements.push({
					tagName: 'img',
					selector: getSelector(img),
					animationType: 'gif',
					duration: 0, // Unknown for GIFs
					isAutoplay: true,
					hasControls: false,
					canPause: false,
					isInfinite: true
				});
			}
		});

		// 3. Detect video elements
		document.querySelectorAll('video').forEach(video => {
			result.elements.push({
				tagName: 'video',
				selector: getSelector(video),
				animationType: 'video',
				duration: video.duration || 0,
				isAutoplay: video.autoplay || false,
				hasControls: video.controls || false,
				canPause: true,
				isInfinite: video.loop || false
			});
		});

		// 4. Detect canvas animations (check for requestAnimationFrame usage)
		document.querySelectorAll('canvas').forEach(canvas => {
			// We can't directly detect if canvas is animated, but we can flag it for review
			result.elements.push({
				tagName: 'canvas',
				selector: getSelector(canvas),
				animationType: 'canvas',
				duration: 0,
				isAutoplay: true, // Assume autoplay for canvas
				hasControls: false,
				canPause: false,
				isInfinite: true // Assume infinite for canvas
			});
		});

		// 5. Detect SVG animations
		document.querySelectorAll('svg animate, svg animateTransform, svg animateMotion').forEach(anim => {
			const svg = anim.closest('svg');
			const dur = anim.getAttribute('dur');
			const repeatCount = anim.getAttribute('repeatCount');

			let duration = 0;
			if (dur) {
				duration = parseFloat(dur.replace('s', ''));
			}

			result.elements.push({
				tagName: 'svg',
				selector: getSelector(svg),
				animationType: 'svg',
				duration: duration,
				isAutoplay: true,
				hasControls: false,
				canPause: false,
				isInfinite: repeatCount === 'indefinite'
			});
		});

		return JSON.stringify(result);
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to detect animations: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("animation detection returned nil result")
	}

	// Parse the JavaScript result
	var animationsData struct {
		Elements []struct {
			TagName       string  `json:"tagName"`
			Selector      string  `json:"selector"`
			AnimationType string  `json:"animationType"`
			Duration      float64 `json:"duration"`
			IsAutoplay    bool    `json:"isAutoplay"`
			HasControls   bool    `json:"hasControls"`
			CanPause      bool    `json:"canPause"`
			IsInfinite    bool    `json:"isInfinite"`
		} `json:"elements"`
	}

	err = json.Unmarshal([]byte(jsResult.Value.Str()), &animationsData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse animations data: %v", err)
	}

	result := &AnimationFlashResult{
		TotalAnimations: len(animationsData.Elements),
		Elements:        make([]AnimationFlashElement, 0),
	}

	// Analyze each animation
	for _, anim := range animationsData.Elements {
		element := AnimationFlashElement{
			TagName:       anim.TagName,
			Selector:      anim.Selector,
			AnimationType: anim.AnimationType,
			Duration:      anim.Duration,
			IsAutoplay:    anim.IsAutoplay,
			HasControls:   anim.HasControls,
			CanPause:      anim.CanPause,
		}

		// Estimate flash rate (simplified - real detection would require frame analysis)
		// For CSS animations with very short durations, assume potential flashing
		if anim.AnimationType == "css" && anim.Duration > 0 && anim.Duration < 0.5 {
			element.FlashRate = 1.0 / anim.Duration
			if element.FlashRate > 3.0 {
				result.FlashingContent++
			}
		}

		// Check for violations
		violations := make([]string, 0)

		// WCAG 2.3.1: Three Flashes or Below Threshold (Level A)
		if element.FlashRate > 3.0 {
			element.IsViolation = true
			element.ViolationType = "flashing_content"
			element.Recommendation = "Flashing content exceeds 3 flashes per second. Reduce flash rate or provide mechanism to disable."
			violations = append(violations, "flashing_content")
			result.Violations++
		}

		// WCAG 2.2.2: Pause, Stop, Hide (Level A)
		if anim.IsAutoplay && !anim.HasControls && !anim.CanPause && anim.Duration > 5 {
			element.IsViolation = true
			element.ViolationType = "no_pause_control"
			element.Recommendation = "Animation plays automatically for more than 5 seconds without pause/stop controls."
			violations = append(violations, "no_pause_control")
			result.Violations++
		}

		// Warning: Rapid animations (not necessarily flashing)
		if anim.AnimationType == "css" && anim.Duration > 0 && anim.Duration < 1.0 && anim.IsInfinite {
			result.RapidAnimations++
			if !element.IsViolation {
				element.ViolationType = "rapid_animation"
				element.Recommendation = "Rapid infinite animation detected. Consider providing pause controls."
				result.Warnings++
			}
		}

		// Count autoplay animations
		if anim.IsAutoplay {
			result.AutoplayAnimations++
		}

		result.Elements = append(result.Elements, element)
	}

	d.debugLog("Successfully detected animation/flash for tab: %s (total: %d, flashing: %d, violations: %d)",
		tabID, result.TotalAnimations, result.FlashingContent, result.Violations)
	return result, nil
}

// EnhancedAccessibilityResult represents enhanced accessibility tree analysis
type EnhancedAccessibilityResult struct {
	TotalElements      int                            `json:"total_elements"`
	ElementsWithIssues int                            `json:"elements_with_issues"`
	ARIAViolations     int                            `json:"aria_violations"`
	RoleViolations     int                            `json:"role_violations"`
	RelationshipIssues int                            `json:"relationship_issues"`
	LandmarkIssues     int                            `json:"landmark_issues"`
	Elements           []EnhancedAccessibilityElement `json:"elements"`
}

// EnhancedAccessibilityElement represents an element with accessibility analysis
type EnhancedAccessibilityElement struct {
	TagName           string   `json:"tag_name"`
	Selector          string   `json:"selector"`
	Role              string   `json:"role"`
	AriaLabel         string   `json:"aria_label"`
	AriaDescribedBy   string   `json:"aria_described_by"`
	AriaLabelledBy    string   `json:"aria_labelled_by"`
	AriaRequired      bool     `json:"aria_required"`
	AriaInvalid       bool     `json:"aria_invalid"`
	AriaHidden        bool     `json:"aria_hidden"`
	TabIndex          int      `json:"tab_index"`
	IsInteractive     bool     `json:"is_interactive"`
	HasAccessibleName bool     `json:"has_accessible_name"`
	Issues            []string `json:"issues"`
	Recommendations   []string `json:"recommendations"`
}

// analyzeEnhancedAccessibility performs enhanced accessibility tree analysis
func (d *Daemon) analyzeEnhancedAccessibility(tabID string, timeout int) (*EnhancedAccessibilityResult, error) {
	d.debugLog("Analyzing enhanced accessibility for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// JavaScript code for enhanced accessibility analysis
	jsCode := `() => {
		const result = {
			elements: []
		};

		// Helper function to get element selector
		function getSelector(element) {
			if (element.id) return '#' + element.id;
			if (element.className && typeof element.className === 'string') {
				const classes = element.className.trim().split(/\s+/).slice(0, 2).join('.');
				if (classes) return element.tagName.toLowerCase() + '.' + classes;
			}
			return element.tagName.toLowerCase();
		}

		// Helper function to get accessible name
		function getAccessibleName(element) {
			// Check aria-label
			if (element.getAttribute('aria-label')) {
				return element.getAttribute('aria-label');
			}
			// Check aria-labelledby
			if (element.getAttribute('aria-labelledby')) {
				const ids = element.getAttribute('aria-labelledby').split(' ');
				const labels = ids.map(id => {
					const el = document.getElementById(id);
					return el ? el.textContent.trim() : '';
				}).filter(t => t);
				if (labels.length > 0) return labels.join(' ');
			}
			// Check label element (for form controls)
			if (element.id) {
				const label = document.querySelector('label[for="' + element.id + '"]');
				if (label) return label.textContent.trim();
			}
			// Check alt attribute (for images)
			if (element.hasAttribute('alt')) {
				return element.getAttribute('alt');
			}
			// Check title attribute
			if (element.hasAttribute('title')) {
				return element.getAttribute('title');
			}
			// Check text content (for buttons, links)
			if (['button', 'a'].includes(element.tagName.toLowerCase())) {
				return element.textContent.trim();
			}
			return '';
		}

		// Interactive elements that should have accessible names
		const interactiveSelectors = 'button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="tab"], [role="menuitem"]';

		document.querySelectorAll(interactiveSelectors).forEach(element => {
			const role = element.getAttribute('role') || element.tagName.toLowerCase();
			const ariaLabel = element.getAttribute('aria-label') || '';
			const ariaDescribedBy = element.getAttribute('aria-describedby') || '';
			const ariaLabelledBy = element.getAttribute('aria-labelledby') || '';
			const ariaRequired = element.getAttribute('aria-required') === 'true';
			const ariaInvalid = element.getAttribute('aria-invalid') === 'true';
			const ariaHidden = element.getAttribute('aria-hidden') === 'true';
			const tabIndex = parseInt(element.getAttribute('tabindex')) || 0;
			const accessibleName = getAccessibleName(element);

			const elementData = {
				tagName: element.tagName.toLowerCase(),
				selector: getSelector(element),
				role: role,
				ariaLabel: ariaLabel,
				ariaDescribedBy: ariaDescribedBy,
				ariaLabelledBy: ariaLabelledBy,
				ariaRequired: ariaRequired,
				ariaInvalid: ariaInvalid,
				ariaHidden: ariaHidden,
				tabIndex: tabIndex,
				isInteractive: true,
				hasAccessibleName: accessibleName.length > 0,
				accessibleName: accessibleName
			};

			result.elements.push(elementData);
		});

		// Check landmarks
		const landmarks = document.querySelectorAll('main, header, footer, nav, aside, section, [role="main"], [role="banner"], [role="contentinfo"], [role="navigation"], [role="complementary"], [role="region"]');
		landmarks.forEach(element => {
			const role = element.getAttribute('role') || element.tagName.toLowerCase();
			const ariaLabel = element.getAttribute('aria-label') || '';
			const ariaLabelledBy = element.getAttribute('aria-labelledby') || '';
			const accessibleName = getAccessibleName(element);

			// Multiple landmarks of same type should have labels
			const sameTypeLandmarks = document.querySelectorAll('[role="' + role + '"], ' + element.tagName.toLowerCase());
			const needsLabel = sameTypeLandmarks.length > 1;

			const elementData = {
				tagName: element.tagName.toLowerCase(),
				selector: getSelector(element),
				role: role,
				ariaLabel: ariaLabel,
				ariaDescribedBy: '',
				ariaLabelledBy: ariaLabelledBy,
				ariaRequired: false,
				ariaInvalid: false,
				ariaHidden: false,
				tabIndex: 0,
				isInteractive: false,
				hasAccessibleName: accessibleName.length > 0,
				accessibleName: accessibleName,
				isLandmark: true,
				needsLabel: needsLabel
			};

			result.elements.push(elementData);
		});

		return JSON.stringify(result);
	}`

	jsResult, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to analyze accessibility: %v", err)
	}
	if jsResult == nil {
		return nil, fmt.Errorf("accessibility analysis returned nil result")
	}

	// Parse the JavaScript result
	var elementsData struct {
		Elements []struct {
			TagName           string `json:"tagName"`
			Selector          string `json:"selector"`
			Role              string `json:"role"`
			AriaLabel         string `json:"ariaLabel"`
			AriaDescribedBy   string `json:"ariaDescribedBy"`
			AriaLabelledBy    string `json:"ariaLabelledBy"`
			AriaRequired      bool   `json:"ariaRequired"`
			AriaInvalid       bool   `json:"ariaInvalid"`
			AriaHidden        bool   `json:"ariaHidden"`
			TabIndex          int    `json:"tabIndex"`
			IsInteractive     bool   `json:"isInteractive"`
			HasAccessibleName bool   `json:"hasAccessibleName"`
			AccessibleName    string `json:"accessibleName"`
			IsLandmark        bool   `json:"isLandmark"`
			NeedsLabel        bool   `json:"needsLabel"`
		} `json:"elements"`
	}

	err = json.Unmarshal([]byte(jsResult.Value.Str()), &elementsData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse accessibility data: %v", err)
	}

	result := &EnhancedAccessibilityResult{
		TotalElements: len(elementsData.Elements),
		Elements:      make([]EnhancedAccessibilityElement, 0),
	}

	// Analyze each element
	for _, elem := range elementsData.Elements {
		element := EnhancedAccessibilityElement{
			TagName:           elem.TagName,
			Selector:          elem.Selector,
			Role:              elem.Role,
			AriaLabel:         elem.AriaLabel,
			AriaDescribedBy:   elem.AriaDescribedBy,
			AriaLabelledBy:    elem.AriaLabelledBy,
			AriaRequired:      elem.AriaRequired,
			AriaInvalid:       elem.AriaInvalid,
			AriaHidden:        elem.AriaHidden,
			TabIndex:          elem.TabIndex,
			IsInteractive:     elem.IsInteractive,
			HasAccessibleName: elem.HasAccessibleName,
			Issues:            make([]string, 0),
			Recommendations:   make([]string, 0),
		}

		// Check for issues
		if elem.IsInteractive && !elem.HasAccessibleName {
			element.Issues = append(element.Issues, "Missing accessible name")
			element.Recommendations = append(element.Recommendations, "Add aria-label, aria-labelledby, or visible text")
			result.ARIAViolations++
			result.ElementsWithIssues++
		}

		if elem.IsLandmark && elem.NeedsLabel && !elem.HasAccessibleName {
			element.Issues = append(element.Issues, "Multiple landmarks of same type without distinguishing labels")
			element.Recommendations = append(element.Recommendations, "Add aria-label to distinguish from other "+elem.Role+" landmarks")
			result.LandmarkIssues++
			result.ElementsWithIssues++
		}

		if elem.AriaHidden && elem.IsInteractive {
			element.Issues = append(element.Issues, "Interactive element is aria-hidden")
			element.Recommendations = append(element.Recommendations, "Remove aria-hidden or make element non-interactive")
			result.ARIAViolations++
			result.ElementsWithIssues++
		}

		if elem.TabIndex < -1 {
			element.Issues = append(element.Issues, "Invalid tabindex value")
			element.Recommendations = append(element.Recommendations, "Use tabindex=\"0\" for focusable or tabindex=\"-1\" for programmatically focusable")
			result.ARIAViolations++
			result.ElementsWithIssues++
		}

		// Check for aria-describedby/aria-labelledby references
		if elem.AriaDescribedBy != "" {
			// Would need to verify IDs exist (simplified here)
			element.Recommendations = append(element.Recommendations, "Verify aria-describedby references exist")
		}

		if elem.AriaLabelledBy != "" {
			// Would need to verify IDs exist (simplified here)
			element.Recommendations = append(element.Recommendations, "Verify aria-labelledby references exist")
		}

		if len(element.Issues) > 0 {
			result.Elements = append(result.Elements, element)
		}
	}

	d.debugLog("Successfully analyzed enhanced accessibility for tab: %s (elements: %d, issues: %d)",
		tabID, result.TotalElements, result.ElementsWithIssues)
	return result, nil
}

// AccessibilityFeatures represents detected accessibility features
type AccessibilityFeatures struct {
	PluginsDetected   []string          `json:"plugins_detected"`
	HasFocusVisible   bool              `json:"has_focus_visible"`
	HasUniversalFocus bool              `json:"has_universal_focus"`
	CSSVariables      map[string]string `json:"css_variables"`
}

// ConfidenceLevel represents the confidence in test results
type ConfidenceLevel struct {
	Level          string `json:"level"` // LOW, MEDIUM, HIGH
	Reason         string `json:"reason"`
	Recommendation string `json:"recommendation"`
}

// KeyboardTestResult represents the result of keyboard navigation testing
type KeyboardTestResult struct {
	TotalInteractive      int                    `json:"total_interactive"`
	Focusable             int                    `json:"focusable"`
	NotFocusable          int                    `json:"not_focusable"`
	NoFocusIndicator      int                    `json:"no_focus_indicator"`
	KeyboardTraps         int                    `json:"keyboard_traps"`
	TabOrder              []KeyboardTestElement  `json:"tab_order"`
	Issues                []KeyboardTestIssue    `json:"issues"`
	AccessibilityFeatures *AccessibilityFeatures `json:"accessibility_features,omitempty"`
	Confidence            *ConfidenceLevel       `json:"confidence,omitempty"`
}

// KeyboardTestElement represents an interactive element in tab order
type KeyboardTestElement struct {
	Index         int    `json:"index"`
	Selector      string `json:"selector"`
	TagName       string `json:"tag_name"`
	Role          string `json:"role"`
	Text          string `json:"text"`
	TabIndex      int    `json:"tab_index"`
	HasFocusStyle bool   `json:"has_focus_style"`
	IsVisible     bool   `json:"is_visible"`
}

// KeyboardTestIssue represents a keyboard accessibility issue
type KeyboardTestIssue struct {
	Type        string `json:"type"`
	Severity    string `json:"severity"`
	Element     string `json:"element"`
	Description string `json:"description"`
}

// detectAccessibilityFeatures detects accessibility plugins and :focus-visible implementations
func (d *Daemon) detectAccessibilityFeatures(page *rod.Page) (*AccessibilityFeatures, error) {
	jsCode := `() => {
		// Check for common accessibility plugins
		const plugins = {
			accessifix: document.querySelectorAll('[class*="accessifix"], [id*="accessifix"]').length > 0,
			userway: document.querySelectorAll('[class*="userway"], [id*="userway"]').length > 0,
			accessibe: document.querySelectorAll('[class*="accessibe"], [id*="accessibe"]').length > 0,
			audioeye: document.querySelectorAll('[class*="audioeye"], [id*="audioeye"]').length > 0,
			equalweb: document.querySelectorAll('[class*="equalweb"], [id*="equalweb"]').length > 0
		};

		const pluginsDetected = Object.keys(plugins).filter(k => plugins[k]);

		// Check for :focus-visible rules
		let hasFocusVisible = false;
		let hasUniversalFocus = false;

		try {
			Array.from(document.styleSheets).forEach(sheet => {
				try {
					Array.from(sheet.cssRules || []).forEach(rule => {
						if (rule.selectorText && rule.selectorText.includes(':focus-visible')) {
							hasFocusVisible = true;
							// Check for universal :focus-visible (*, html body, etc.)
							if (rule.selectorText.match(/^(\*|html\s+body)\s*:focus-visible/)) {
								hasUniversalFocus = true;
							}
						}
					});
				} catch(e) {
					// CORS or other stylesheet access error - ignore
				}
			});
		} catch(e) {
			// Error accessing stylesheets - ignore
		}

		// Check for CSS custom properties (Accessifix-specific)
		const root = getComputedStyle(document.documentElement);
		const cssVariables = {};

		const focusWidth = root.getPropertyValue('--accessifix-focus-width');
		const focusColor = root.getPropertyValue('--accessifix-focus-color');
		const focusStyle = root.getPropertyValue('--accessifix-focus-style');

		if (focusWidth) cssVariables.width = focusWidth.trim();
		if (focusColor) cssVariables.color = focusColor.trim();
		if (focusStyle) cssVariables.style = focusStyle.trim();

		return JSON.stringify({
			plugins_detected: pluginsDetected,
			has_focus_visible: hasFocusVisible,
			has_universal_focus: hasUniversalFocus,
			css_variables: cssVariables
		});
	}`

	result, err := page.Eval(jsCode)
	if err != nil {
		return nil, fmt.Errorf("failed to detect accessibility features: %w", err)
	}
	if result == nil {
		return nil, fmt.Errorf("accessibility features detection returned nil result")
	}

	var features AccessibilityFeatures
	err = json.Unmarshal([]byte(result.Value.String()), &features)
	if err != nil {
		return nil, fmt.Errorf("failed to parse accessibility features: %w", err)
	}

	return &features, nil
}

// calculateConfidence calculates confidence level based on detected accessibility features
func (d *Daemon) calculateConfidence(features *AccessibilityFeatures, noFocusIndicator int) *ConfidenceLevel {
	// Low confidence: Plugin or universal :focus-visible detected
	if len(features.PluginsDetected) > 0 || features.HasUniversalFocus {
		pluginList := "accessibility plugin"
		if len(features.PluginsDetected) > 0 {
			pluginList = strings.Join(features.PluginsDetected, ", ")
		}

		return &ConfidenceLevel{
			Level:          "LOW",
			Reason:         fmt.Sprintf("Detected %s or universal :focus-visible implementation", pluginList),
			Recommendation: "Manual keyboard testing required for validation. Automated tests may not accurately detect :focus-visible implementations.",
		}
	}

	// Medium confidence: Partial :focus-visible implementation
	if features.HasFocusVisible && !features.HasUniversalFocus {
		return &ConfidenceLevel{
			Level:          "MEDIUM",
			Reason:         "Partial :focus-visible implementation detected",
			Recommendation: "Manual validation recommended for elements with :focus-visible styles.",
		}
	}

	// High confidence: No special implementations detected
	return &ConfidenceLevel{
		Level:          "HIGH",
		Reason:         "No accessibility plugins or :focus-visible detected",
		Recommendation: "Results likely accurate. Standard focus indicators detected.",
	}
}

// testKeyboardNavigationWithRealKeys tests keyboard navigation using real Tab key presses
// This properly triggers :focus-within and other CSS pseudo-classes
func (d *Daemon) testKeyboardNavigationWithRealKeys(tabID string, timeout int) (*KeyboardTestResult, error) {
	d.debugLog("Testing keyboard navigation with real Tab key simulation for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Detect accessibility features first
	features, err := d.detectAccessibilityFeatures(page)
	if err != nil {
		d.debugLog("Warning: failed to detect accessibility features: %v", err)
		// Continue with testing even if detection fails
		features = &AccessibilityFeatures{
			PluginsDetected: []string{},
			CSSVariables:    make(map[string]string),
		}
	}

	// First, get all interactive elements and their info
	jsCode := `() => {
		const results = {
			total_interactive: 0,
			focusable: 0,
			not_focusable: 0,
			no_focus_indicator: 0,
			keyboard_traps: 0,
			tab_order: [],
			issues: [],
			interactive_elements: []
		};

		// Helper to check if element is visible
		function isVisible(element) {
			const style = window.getComputedStyle(element);
			return style.display !== 'none' &&
				   style.visibility !== 'hidden' &&
				   style.opacity !== '0' &&
				   element.offsetWidth > 0 &&
				   element.offsetHeight > 0;
		}

		// Helper to get element selector
		function getSelector(element) {
			if (element.id) return '#' + element.id;
			if (element.className && typeof element.className === 'string') {
				const classes = element.className.trim().split(/\s+/).slice(0, 2).join('.');
				if (classes) return element.tagName.toLowerCase() + '.' + classes;
			}
			return element.tagName.toLowerCase();
		}

		// Get all interactive elements
		const interactiveSelectors = [
			'a[href]',
			'button',
			'input:not([type="hidden"])',
			'select',
			'textarea',
			'[tabindex]:not([tabindex="-1"])',
			'[role="button"]',
			'[role="link"]',
			'[role="checkbox"]',
			'[role="radio"]',
			'[role="tab"]',
			'[role="menuitem"]'
		];

		const allInteractive = document.querySelectorAll(interactiveSelectors.join(','));
		results.total_interactive = allInteractive.length;

		// Store info about each interactive element
		allInteractive.forEach((element) => {
			const visible = isVisible(element);
			const selector = getSelector(element);
			const tagName = element.tagName.toLowerCase();
			const role = element.getAttribute('role') || '';
			const text = element.textContent.trim().substring(0, 50);
			const tabIndex = element.tabIndex;

			// Skip elements that are not in the natural tab order
			// Elements with tabIndex < 0 are programmatically focusable but not reachable via Tab key
			if (tabIndex < 0) {
				return;
			}

			// Check if element is focusable using programmatic focus
			let isFocusable = false;
			try {
				element.focus();
				isFocusable = document.activeElement === element;
				element.blur();
			} catch (e) {
				// Element not focusable
			}

			if (visible) {
				if (isFocusable) {
					results.focusable++;
					results.interactive_elements.push({
						selector: selector,
						tag_name: tagName,
						role: role,
						text: text,
						tab_index: tabIndex,
						is_visible: visible
					});
				} else {
					results.not_focusable++;
					results.issues.push({
						type: 'not_focusable',
						severity: 'high',
						element: selector,
						description: 'Interactive element is not keyboard focusable'
					});
				}
			}
		});

		return JSON.stringify(results);
	}`

	// Execute the initial scan
	var jsResult *proto.RuntimeRemoteObject
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			result *proto.RuntimeRemoteObject
			err    error
		}, 1)

		go func() {
			result, err := page.Eval(jsCode)
			done <- struct {
				result *proto.RuntimeRemoteObject
				err    error
			}{result, err}
		}()

		select {
		case res := <-done:
			if res.err != nil {
				return nil, fmt.Errorf("failed to scan interactive elements: %w", res.err)
			}
			jsResult = res.result
		case <-ctx.Done():
			return nil, fmt.Errorf("keyboard navigation test timed out after %d seconds", timeout)
		}
	} else {
		jsResult, err = page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to scan interactive elements: %w", err)
		}
	}

	if jsResult == nil {
		return nil, fmt.Errorf("interactive elements scan returned nil result")
	}

	// Parse initial results
	var initialResult KeyboardTestResult
	err = json.Unmarshal([]byte(jsResult.Value.String()), &initialResult)
	if err != nil {
		return nil, fmt.Errorf("failed to parse initial scan results: %w", err)
	}

	d.debugLog("Found %d focusable elements, now testing with real Tab key presses", initialResult.Focusable)

	// Now use real Tab key presses to test focus indicators
	// Focus the body first to start from a known state
	_, err = page.Eval(`() => { document.body.focus(); }`)
	if err != nil {
		d.debugLog("Warning: failed to focus body: %v", err)
	}

	// Wait a bit for any animations
	time.Sleep(100 * time.Millisecond)

	// Press Tab key to navigate through focusable elements
	tabCount := 0
	maxTabs := initialResult.Focusable + 10 // Add buffer for safety
	if maxTabs > 200 {
		maxTabs = 200 // Cap at 200 to prevent infinite loops
	}

	for tabCount < maxTabs {
		// Press Tab key
		err = d.performSpecialKey(tabID, "Tab")
		if err != nil {
			d.debugLog("Warning: failed to press Tab key: %v", err)
			break
		}

		// Small delay to let focus settle and animations complete
		time.Sleep(50 * time.Millisecond)

		// Check current focused element and its focus indicator
		checkCode := `() => {
			const activeEl = document.activeElement;
			if (!activeEl || activeEl === document.body || activeEl === document.documentElement) {
				return JSON.stringify({ done: true });
			}

			function getSelector(element) {
				if (element.id) return '#' + element.id;
				if (element.className && typeof element.className === 'string') {
					const classes = element.className.trim().split(/\s+/).slice(0, 2).join('.');
					if (classes) return element.tagName.toLowerCase() + '.' + classes;
				}
				return element.tagName.toLowerCase();
			}

			function isVisible(element) {
				const style = window.getComputedStyle(element);
				return style.display !== 'none' &&
					   style.visibility !== 'hidden' &&
					   style.opacity !== '0' &&
					   element.offsetWidth > 0 &&
					   element.offsetHeight > 0;
			}

			const style = window.getComputedStyle(activeEl);
			const selector = getSelector(activeEl);
			const visible = isVisible(activeEl);

			// Check for visible focus indicator
			const hasFocusIndicator = (
				(style.outlineWidth && parseFloat(style.outlineWidth) > 0 && style.outlineStyle !== 'none') ||
				(style.boxShadow && style.boxShadow !== 'none') ||
				(style.border && style.borderWidth && parseFloat(style.borderWidth) > 0)
			);

			return JSON.stringify({
				done: false,
				selector: selector,
				tag_name: activeEl.tagName.toLowerCase(),
				role: activeEl.getAttribute('role') || '',
				text: activeEl.textContent.trim().substring(0, 50),
				tab_index: activeEl.tabIndex,
				has_focus_indicator: hasFocusIndicator,
				is_visible: visible,
				outline_width: style.outlineWidth,
				box_shadow: style.boxShadow,
				border_width: style.borderWidth
			});
		}`

		checkResult, err := page.Eval(checkCode)
		if err != nil {
			d.debugLog("Warning: failed to check focused element: %v", err)
			break
		}
		if checkResult == nil {
			d.debugLog("Warning: focus check returned nil result")
			break
		}

		var focusInfo struct {
			Done              bool   `json:"done"`
			Selector          string `json:"selector"`
			TagName           string `json:"tag_name"`
			Role              string `json:"role"`
			Text              string `json:"text"`
			TabIndex          int    `json:"tab_index"`
			HasFocusIndicator bool   `json:"has_focus_indicator"`
			IsVisible         bool   `json:"is_visible"`
			OutlineWidth      string `json:"outline_width"`
			BoxShadow         string `json:"box_shadow"`
			BorderWidth       string `json:"border_width"`
		}

		err = json.Unmarshal([]byte(checkResult.Value.String()), &focusInfo)
		if err != nil {
			d.debugLog("Warning: failed to parse focus info: %v", err)
			break
		}

		// If we've cycled back to body/document, we're done
		if focusInfo.Done {
			break
		}

		// Add to tab order
		initialResult.TabOrder = append(initialResult.TabOrder, KeyboardTestElement{
			Index:         len(initialResult.TabOrder),
			Selector:      focusInfo.Selector,
			TagName:       focusInfo.TagName,
			Role:          focusInfo.Role,
			Text:          focusInfo.Text,
			TabIndex:      focusInfo.TabIndex,
			HasFocusStyle: focusInfo.HasFocusIndicator,
			IsVisible:     focusInfo.IsVisible,
		})

		// Track elements without focus indicators
		if !focusInfo.HasFocusIndicator && focusInfo.IsVisible {
			initialResult.NoFocusIndicator++
			initialResult.Issues = append(initialResult.Issues, KeyboardTestIssue{
				Type:        "no_focus_indicator",
				Severity:    "high",
				Element:     focusInfo.Selector,
				Description: "Interactive element lacks visible focus indicator",
			})
		}

		tabCount++

		// Safety check: if we've found all expected focusable elements, stop
		if len(initialResult.TabOrder) >= initialResult.Focusable {
			break
		}
	}

	d.debugLog("Completed keyboard navigation test: %d elements in tab order, %d without focus indicators",
		len(initialResult.TabOrder), initialResult.NoFocusIndicator)

	// Add accessibility features and confidence to result
	initialResult.AccessibilityFeatures = features
	initialResult.Confidence = d.calculateConfidence(features, initialResult.NoFocusIndicator)

	d.debugLog("Accessibility features detected: plugins=%v, focus-visible=%v, universal=%v, confidence=%s",
		features.PluginsDetected, features.HasFocusVisible, features.HasUniversalFocus, initialResult.Confidence.Level)

	return &initialResult, nil
}

// ZoomTestResult represents the result of zoom level testing
type ZoomTestResult struct {
	ZoomLevels []ZoomLevelTest `json:"zoom_levels"`
	Issues     []ZoomTestIssue `json:"issues"`
}

// ZoomLevelTest represents testing at a specific zoom level
type ZoomLevelTest struct {
	ZoomLevel           float64 `json:"zoom_level"`
	ViewportWidth       int     `json:"viewport_width"`
	ViewportHeight      int     `json:"viewport_height"`
	HasHorizontalScroll bool    `json:"has_horizontal_scroll"`
	ContentWidth        int     `json:"content_width"`
	ContentHeight       int     `json:"content_height"`
	VisibleElements     int     `json:"visible_elements"`
	OverflowingElements int     `json:"overflowing_elements"`
	TextReadable        bool    `json:"text_readable"`
}

// ZoomTestIssue represents an issue found during zoom testing
type ZoomTestIssue struct {
	ZoomLevel   float64 `json:"zoom_level"`
	Type        string  `json:"type"`
	Severity    string  `json:"severity"`
	Description string  `json:"description"`
	Element     string  `json:"element,omitempty"`
}

// testZoom tests page at different zoom levels
func (d *Daemon) testZoom(tabID string, zoomLevels []float64, timeout int) (*ZoomTestResult, error) {
	d.debugLog("Testing zoom levels for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Default zoom levels if none provided
	if len(zoomLevels) == 0 {
		zoomLevels = []float64{1.0, 2.0, 4.0}
	}

	result := &ZoomTestResult{
		ZoomLevels: make([]ZoomLevelTest, 0, len(zoomLevels)),
		Issues:     make([]ZoomTestIssue, 0),
	}

	// Get original viewport size
	originalViewport, err := page.Eval(`() => {
		return JSON.stringify({
			width: window.innerWidth,
			height: window.innerHeight
		});
	}`)
	if err != nil {
		return nil, fmt.Errorf("failed to get viewport size: %w", err)
	}
	if originalViewport == nil {
		return nil, fmt.Errorf("viewport size evaluation returned nil result")
	}

	var viewportData struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	}
	err = json.Unmarshal([]byte(originalViewport.Value.Str()), &viewportData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse viewport data: %w", err)
	}

	// Test each zoom level
	for _, zoom := range zoomLevels {
		d.debugLog("Testing zoom level: %.1f", zoom)

		// Set zoom level using Emulation domain
		err = page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{
			Width:             viewportData.Width,
			Height:            viewportData.Height,
			DeviceScaleFactor: zoom,
			Mobile:            false,
		})
		if err != nil {
			d.debugLog("Failed to set zoom level %.1f: %v", zoom, err)
			result.Issues = append(result.Issues, ZoomTestIssue{
				ZoomLevel:   zoom,
				Type:        "zoom_error",
				Severity:    "high",
				Description: fmt.Sprintf("Failed to set zoom level: %v", err),
			})
			continue
		}

		// Wait a moment for reflow
		time.Sleep(500 * time.Millisecond)

		// JavaScript to analyze page at this zoom level
		jsCode := `() => {
			const body = document.body;
			const html = document.documentElement;

			// Get content dimensions
			const contentWidth = Math.max(
				body.scrollWidth, body.offsetWidth,
				html.clientWidth, html.scrollWidth, html.offsetWidth
			);
			const contentHeight = Math.max(
				body.scrollHeight, body.offsetHeight,
				html.clientHeight, html.scrollHeight, html.offsetHeight
			);

			// Check for horizontal scroll
			const hasHorizontalScroll = contentWidth > window.innerWidth;

			// Count visible elements
			const allElements = document.querySelectorAll('*');
			let visibleCount = 0;
			let overflowingCount = 0;

			allElements.forEach(el => {
				const style = window.getComputedStyle(el);
				const rect = el.getBoundingClientRect();

				if (style.display !== 'none' && style.visibility !== 'hidden' &&
					rect.width > 0 && rect.height > 0) {
					visibleCount++;

					// Check if element overflows viewport
					if (rect.right > window.innerWidth || rect.left < 0) {
						overflowingCount++;
					}
				}
			});

			// Check text readability (minimum font size)
			const textElements = document.querySelectorAll('p, span, div, a, button, li, td, th, label');
			let minFontSize = Infinity;
			textElements.forEach(el => {
				const style = window.getComputedStyle(el);
				const fontSize = parseFloat(style.fontSize);
				if (fontSize > 0 && fontSize < minFontSize) {
					minFontSize = fontSize;
				}
			});

			// Text is readable if minimum font size is at least 9px (WCAG recommendation)
			const textReadable = minFontSize >= 9;

			return JSON.stringify({
				viewport_width: window.innerWidth,
				viewport_height: window.innerHeight,
				has_horizontal_scroll: hasHorizontalScroll,
				content_width: contentWidth,
				content_height: contentHeight,
				visible_elements: visibleCount,
				overflowing_elements: overflowingCount,
				text_readable: textReadable,
				min_font_size: minFontSize
			});
		}`

		var jsResult *proto.RuntimeRemoteObject
		if timeout > 0 {
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
			defer cancel()

			done := make(chan struct {
				result *proto.RuntimeRemoteObject
				err    error
			}, 1)

			go func() {
				res, err := page.Eval(jsCode)
				done <- struct {
					result *proto.RuntimeRemoteObject
					err    error
				}{res, err}
			}()

			select {
			case res := <-done:
				if res.err != nil {
					result.Issues = append(result.Issues, ZoomTestIssue{
						ZoomLevel:   zoom,
						Type:        "evaluation_error",
						Severity:    "high",
						Description: fmt.Sprintf("Failed to evaluate page: %v", res.err),
					})
					continue
				}
				jsResult = res.result
			case <-ctx.Done():
				result.Issues = append(result.Issues, ZoomTestIssue{
					ZoomLevel:   zoom,
					Type:        "timeout",
					Severity:    "high",
					Description: fmt.Sprintf("Evaluation timed out after %d seconds", timeout),
				})
				continue
			}
		} else {
			jsResult, err = page.Eval(jsCode)
			if err != nil {
				result.Issues = append(result.Issues, ZoomTestIssue{
					ZoomLevel:   zoom,
					Type:        "evaluation_error",
					Severity:    "high",
					Description: fmt.Sprintf("Failed to evaluate page: %v", err),
				})
				continue
			}
		}

		if jsResult == nil {
			result.Issues = append(result.Issues, ZoomTestIssue{
				ZoomLevel:   zoom,
				Type:        "nil_result",
				Severity:    "high",
				Description: "Evaluation returned nil result",
			})
			continue
		}

		// Parse the results
		var zoomTest ZoomLevelTest
		resultStr := jsResult.Value.Str()
		d.debugLog("Zoom test result string: %s", resultStr)
		err = json.Unmarshal([]byte(resultStr), &zoomTest)
		if err != nil {
			result.Issues = append(result.Issues, ZoomTestIssue{
				ZoomLevel:   zoom,
				Type:        "parse_error",
				Severity:    "high",
				Description: fmt.Sprintf("Failed to parse results (got: %s): %v", resultStr, err),
			})
			continue
		}

		zoomTest.ZoomLevel = zoom
		result.ZoomLevels = append(result.ZoomLevels, zoomTest)

		// Check for issues
		if zoomTest.HasHorizontalScroll {
			result.Issues = append(result.Issues, ZoomTestIssue{
				ZoomLevel:   zoom,
				Type:        "horizontal_scroll",
				Severity:    "medium",
				Description: "Page has horizontal scrollbar (WCAG 1.4.10 violation)",
			})
		}

		if zoomTest.OverflowingElements > 0 {
			result.Issues = append(result.Issues, ZoomTestIssue{
				ZoomLevel:   zoom,
				Type:        "overflowing_content",
				Severity:    "medium",
				Description: fmt.Sprintf("%d elements overflow viewport", zoomTest.OverflowingElements),
			})
		}

		if !zoomTest.TextReadable {
			result.Issues = append(result.Issues, ZoomTestIssue{
				ZoomLevel:   zoom,
				Type:        "text_too_small",
				Severity:    "high",
				Description: "Text size too small for readability",
			})
		}
	}

	// Reset viewport to original
	err = page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{
		Width:             viewportData.Width,
		Height:            viewportData.Height,
		DeviceScaleFactor: 1.0,
		Mobile:            false,
	})
	if err != nil {
		d.debugLog("Warning: Failed to reset viewport: %v", err)
	}

	d.debugLog("Successfully tested zoom levels for tab: %s (found %d issues)", tabID, len(result.Issues))
	return result, nil
}

// ReflowTestResult represents the result of reflow/responsive testing
type ReflowTestResult struct {
	Breakpoints []ReflowBreakpoint `json:"breakpoints"`
	Issues      []ReflowTestIssue  `json:"issues"`
}

// ReflowBreakpoint represents testing at a specific viewport width
type ReflowBreakpoint struct {
	Width               int  `json:"width"`
	Height              int  `json:"height"`
	HasHorizontalScroll bool `json:"has_horizontal_scroll"`
	ContentWidth        int  `json:"content_width"`
	ContentHeight       int  `json:"content_height"`
	VisibleElements     int  `json:"visible_elements"`
	OverflowingElements int  `json:"overflowing_elements"`
	ResponsiveLayout    bool `json:"responsive_layout"`
}

// ReflowTestIssue represents an issue found during reflow testing
type ReflowTestIssue struct {
	Width       int    `json:"width"`
	Type        string `json:"type"`
	Severity    string `json:"severity"`
	Description string `json:"description"`
	Element     string `json:"element,omitempty"`
}

// testReflow tests page at different viewport widths for responsive design
func (d *Daemon) testReflow(tabID string, widths []int, timeout int) (*ReflowTestResult, error) {
	d.debugLog("Testing reflow at different widths for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Default widths if none provided (WCAG 1.4.10 breakpoints)
	if len(widths) == 0 {
		widths = []int{320, 1280}
	}

	result := &ReflowTestResult{
		Breakpoints: make([]ReflowBreakpoint, 0, len(widths)),
		Issues:      make([]ReflowTestIssue, 0),
	}

	// Get original viewport size
	originalViewport, err := page.Eval(`() => {
		return JSON.stringify({
			width: window.innerWidth,
			height: window.innerHeight
		});
	}`)
	if err != nil {
		return nil, fmt.Errorf("failed to get viewport size: %w", err)
	}
	if originalViewport == nil {
		return nil, fmt.Errorf("viewport size evaluation returned nil result")
	}

	var viewportData struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	}
	err = json.Unmarshal([]byte(originalViewport.Value.Str()), &viewportData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse viewport data: %w", err)
	}

	// Test each width
	for _, width := range widths {
		d.debugLog("Testing width: %dpx", width)

		// Set viewport width
		err = page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{
			Width:             width,
			Height:            viewportData.Height,
			DeviceScaleFactor: 1.0,
			Mobile:            width <= 768, // Consider mobile for small widths
		})
		if err != nil {
			d.debugLog("Failed to set width %d: %v", width, err)
			result.Issues = append(result.Issues, ReflowTestIssue{
				Width:       width,
				Type:        "viewport_error",
				Severity:    "high",
				Description: fmt.Sprintf("Failed to set viewport width: %v", err),
			})
			continue
		}

		// Wait for reflow
		time.Sleep(500 * time.Millisecond)

		// JavaScript to analyze page at this width
		jsCode := `() => {
			const body = document.body;
			const html = document.documentElement;

			// Get content dimensions
			const contentWidth = Math.max(
				body.scrollWidth, body.offsetWidth,
				html.clientWidth, html.scrollWidth, html.offsetWidth
			);
			const contentHeight = Math.max(
				body.scrollHeight, body.offsetHeight,
				html.clientHeight, html.scrollHeight, html.offsetHeight
			);

			// Check for horizontal scroll
			const hasHorizontalScroll = contentWidth > window.innerWidth;

			// Count visible and overflowing elements
			const allElements = document.querySelectorAll('*');
			let visibleCount = 0;
			let overflowingCount = 0;

			allElements.forEach(el => {
				const style = window.getComputedStyle(el);
				const rect = el.getBoundingClientRect();

				if (style.display !== 'none' && style.visibility !== 'hidden' &&
					rect.width > 0 && rect.height > 0) {
					visibleCount++;

					// Check if element overflows viewport
					if (rect.right > window.innerWidth + 5 || rect.left < -5) {
						overflowingCount++;
					}
				}
			});

			// Check if layout appears responsive
			// (content width should not significantly exceed viewport width)
			const responsiveLayout = contentWidth <= window.innerWidth + 20;

			return JSON.stringify({
				width: window.innerWidth,
				height: window.innerHeight,
				has_horizontal_scroll: hasHorizontalScroll,
				content_width: contentWidth,
				content_height: contentHeight,
				visible_elements: visibleCount,
				overflowing_elements: overflowingCount,
				responsive_layout: responsiveLayout
			});
		}`

		var jsResult *proto.RuntimeRemoteObject
		if timeout > 0 {
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
			defer cancel()

			done := make(chan struct {
				result *proto.RuntimeRemoteObject
				err    error
			}, 1)

			go func() {
				res, err := page.Eval(jsCode)
				done <- struct {
					result *proto.RuntimeRemoteObject
					err    error
				}{res, err}
			}()

			select {
			case res := <-done:
				if res.err != nil {
					result.Issues = append(result.Issues, ReflowTestIssue{
						Width:       width,
						Type:        "evaluation_error",
						Severity:    "high",
						Description: fmt.Sprintf("Failed to evaluate page: %v", res.err),
					})
					continue
				}
				jsResult = res.result
			case <-ctx.Done():
				result.Issues = append(result.Issues, ReflowTestIssue{
					Width:       width,
					Type:        "timeout",
					Severity:    "high",
					Description: fmt.Sprintf("Evaluation timed out after %d seconds", timeout),
				})
				continue
			}
		} else {
			jsResult, err = page.Eval(jsCode)
			if err != nil {
				result.Issues = append(result.Issues, ReflowTestIssue{
					Width:       width,
					Type:        "evaluation_error",
					Severity:    "high",
					Description: fmt.Sprintf("Failed to evaluate page: %v", err),
				})
				continue
			}
		}

		if jsResult == nil {
			result.Issues = append(result.Issues, ReflowTestIssue{
				Width:       width,
				Type:        "nil_result",
				Severity:    "high",
				Description: "Evaluation returned nil result",
			})
			continue
		}

		// Parse the results
		var breakpoint ReflowBreakpoint
		err = json.Unmarshal([]byte(jsResult.Value.Str()), &breakpoint)
		if err != nil {
			result.Issues = append(result.Issues, ReflowTestIssue{
				Width:       width,
				Type:        "parse_error",
				Severity:    "high",
				Description: fmt.Sprintf("Failed to parse results: %v", err),
			})
			continue
		}

		result.Breakpoints = append(result.Breakpoints, breakpoint)

		// Check for issues
		if breakpoint.HasHorizontalScroll {
			result.Issues = append(result.Issues, ReflowTestIssue{
				Width:       width,
				Type:        "horizontal_scroll",
				Severity:    "high",
				Description: "Page requires horizontal scrolling (WCAG 1.4.10 violation)",
			})
		}

		if !breakpoint.ResponsiveLayout {
			result.Issues = append(result.Issues, ReflowTestIssue{
				Width:       width,
				Type:        "non_responsive",
				Severity:    "high",
				Description: fmt.Sprintf("Content width (%dpx) exceeds viewport width (%dpx)", breakpoint.ContentWidth, breakpoint.Width),
			})
		}

		if breakpoint.OverflowingElements > 0 {
			result.Issues = append(result.Issues, ReflowTestIssue{
				Width:       width,
				Type:        "overflowing_content",
				Severity:    "medium",
				Description: fmt.Sprintf("%d elements overflow viewport", breakpoint.OverflowingElements),
			})
		}
	}

	// Reset viewport to original
	err = page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{
		Width:             viewportData.Width,
		Height:            viewportData.Height,
		DeviceScaleFactor: 1.0,
		Mobile:            false,
	})
	if err != nil {
		d.debugLog("Warning: Failed to reset viewport: %v", err)
	}

	d.debugLog("Successfully tested reflow for tab: %s (found %d issues)", tabID, len(result.Issues))
	return result, nil
}

// PageAccessibilityReport represents a comprehensive accessibility assessment
type PageAccessibilityReport struct {
	URL              string                 `json:"url"`
	Timestamp        string                 `json:"timestamp"`
	ComplianceStatus string                 `json:"compliance_status"`
	OverallScore     int                    `json:"overall_score"`
	LegalRisk        string                 `json:"legal_risk"`
	CriticalIssues   []AccessibilityIssue   `json:"critical_issues"`
	SeriousIssues    []AccessibilityIssue   `json:"serious_issues"`
	HighIssues       []AccessibilityIssue   `json:"high_issues"`
	MediumIssues     []AccessibilityIssue   `json:"medium_issues"`
	SummaryByWCAG    map[string]WCAGSummary `json:"summary_by_wcag"`
	ContrastSummary  ContrastSummary        `json:"contrast_summary"`
	KeyboardSummary  KeyboardSummary        `json:"keyboard_summary"`
	ARIASummary      ARIASummary            `json:"aria_summary"`
	FormSummary      *FormSummary           `json:"form_summary,omitempty"`
	Screenshots      map[string]string      `json:"screenshots,omitempty"`
	EstimatedHours   int                    `json:"estimated_remediation_hours"`
}

// AccessibilityIssue represents a single accessibility issue
type AccessibilityIssue struct {
	WCAG        string   `json:"wcag"`
	Title       string   `json:"title"`
	Description string   `json:"description"`
	Impact      string   `json:"impact"`
	Count       int      `json:"count"`
	Examples    []string `json:"examples,omitempty"`
	Remediation string   `json:"remediation"`
}

// WCAGSummary represents violations grouped by WCAG principle
type WCAGSummary struct {
	Violations int    `json:"violations"`
	Severity   string `json:"severity"`
}

// ContrastSummary represents a summary of contrast check results
type ContrastSummary struct {
	TotalChecked     int                       `json:"total_checked"`
	Passed           int                       `json:"passed"`
	Failed           int                       `json:"failed"`
	PassRate         string                    `json:"pass_rate"`
	CriticalFailures []ContrastFailure         `json:"critical_failures"`
	FailurePatterns  map[string]FailurePattern `json:"failure_patterns"`
}

// ContrastFailure represents a critical contrast failure
type ContrastFailure struct {
	Selector string  `json:"selector"`
	Text     string  `json:"text"`
	Ratio    float64 `json:"ratio"`
	Required float64 `json:"required"`
	FgColor  string  `json:"fg_color"`
	BgColor  string  `json:"bg_color"`
	Fix      string  `json:"fix"`
}

// FailurePattern represents a pattern of similar failures
type FailurePattern struct {
	Count int     `json:"count"`
	Ratio float64 `json:"ratio"`
	Fix   string  `json:"fix"`
}

// KeyboardSummary represents a summary of keyboard navigation results
type KeyboardSummary struct {
	TotalInteractive      int             `json:"total_interactive"`
	Focusable             int             `json:"focusable"`
	MissingFocusIndicator int             `json:"missing_focus_indicator"`
	KeyboardTraps         int             `json:"keyboard_traps"`
	TabOrderIssues        int             `json:"tab_order_issues"`
	Issues                []KeyboardIssue `json:"issues"`
}

// KeyboardIssue represents a keyboard accessibility issue
type KeyboardIssue struct {
	Type        string   `json:"type"`
	Severity    string   `json:"severity"`
	Count       int      `json:"count"`
	Description string   `json:"description"`
	Fix         string   `json:"fix"`
	Examples    []string `json:"examples,omitempty"`
}

// ARIASummary represents a summary of ARIA validation results
type ARIASummary struct {
	TotalViolations   int         `json:"total_violations"`
	MissingNames      int         `json:"missing_names"`
	InvalidAttributes int         `json:"invalid_attributes"`
	HiddenInteractive int         `json:"hidden_interactive"`
	Issues            []ARIAIssue `json:"issues"`
}

// ARIAIssue represents an ARIA accessibility issue
type ARIAIssue struct {
	Type        string   `json:"type"`
	Severity    string   `json:"severity"`
	Count       int      `json:"count"`
	Description string   `json:"description"`
	Fix         string   `json:"fix"`
	Examples    []string `json:"examples,omitempty"`
}

// FormSummary represents a summary of form accessibility
type FormSummary struct {
	FormsFound int         `json:"forms_found"`
	Forms      []FormAudit `json:"forms"`
}

// FormAudit represents accessibility audit of a single form
type FormAudit struct {
	ID                 string      `json:"id"`
	Fields             int         `json:"fields"`
	Issues             []FormIssue `json:"issues"`
	ARIACompliance     string      `json:"aria_compliance"`
	KeyboardAccessible bool        `json:"keyboard_accessible"`
	RequiredMarked     bool        `json:"required_fields_marked"`
}

// FormIssue represents a form accessibility issue
type FormIssue struct {
	Type        string  `json:"type"`
	Severity    string  `json:"severity"`
	Count       int     `json:"count,omitempty"`
	Description string  `json:"description"`
	Fix         string  `json:"fix"`
	Ratio       float64 `json:"ratio,omitempty"`
}

// getPageAccessibilityReport performs a comprehensive accessibility assessment
func (d *Daemon) getPageAccessibilityReport(tabID string, tests []string, standard string, includeScreenshots bool, timeout int) (*PageAccessibilityReport, error) {
	d.debugLog("Getting page accessibility report for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Get current URL
	url := page.MustInfo().URL

	// Initialize report
	report := &PageAccessibilityReport{
		URL:           url,
		Timestamp:     time.Now().Format(time.RFC3339),
		SummaryByWCAG: make(map[string]WCAGSummary),
		Screenshots:   make(map[string]string),
	}

	// Run tests based on requested types
	runAll := len(tests) == 0 || (len(tests) == 1 && tests[0] == "all")

	// Run axe-core tests if requested
	if runAll || contains(tests, "wcag") {
		d.debugLog("Running axe-core WCAG tests...")
		axeResult, err := d.runAxeCore(tabID, map[string]interface{}{
			"runOnly": map[string]interface{}{
				"type":   "tag",
				"values": []string{"wcag2a", "wcag2aa", "wcag21aa"},
			},
		}, timeout)
		if err == nil {
			d.processAxeResults(report, axeResult)
		}
	}

	// Run contrast check if requested
	if runAll || contains(tests, "contrast") {
		d.debugLog("Running contrast check...")
		contrastResult, err := d.checkContrast(tabID, "", timeout)
		if err == nil {
			d.processContrastResults(report, contrastResult)
		}
	}

	// Run keyboard test if requested (use real keys for accurate :focus-visible detection)
	if runAll || contains(tests, "keyboard") {
		d.debugLog("Running keyboard navigation test with real Tab key simulation...")
		keyboardResult, err := d.testKeyboardNavigationWithRealKeys(tabID, timeout)
		if err == nil {
			d.processKeyboardResults(report, keyboardResult)
		}
	}

	// Run form analysis if requested
	if runAll || contains(tests, "forms") {
		d.debugLog("Running form accessibility audit...")
		formResult, err := d.getFormAccessibilityAudit(tabID, "", timeout)
		if err == nil {
			report.FormSummary = formResult
		}
	}

	// Calculate overall score and compliance status
	d.calculateOverallScore(report)

	d.debugLog("Successfully generated page accessibility report for tab: %s", tabID)
	return report, nil
}

// Helper function to check if slice contains string
func contains(slice []string, str string) bool {
	for _, s := range slice {
		if s == str {
			return true
		}
	}
	return false
}

// processAxeResults processes axe-core results and adds them to the report
func (d *Daemon) processAxeResults(report *PageAccessibilityReport, axeResult *AxeResults) {
	// Process violations by severity
	for _, violation := range axeResult.Violations {
		issue := AccessibilityIssue{
			WCAG:        extractWCAGCriteria(violation.Tags),
			Title:       violation.Help,
			Description: violation.Description,
			Impact:      violation.Impact,
			Count:       len(violation.Nodes),
			Remediation: violation.HelpURL,
		}

		// Add examples (limit to 3)
		for i, node := range violation.Nodes {
			if i >= 3 {
				break
			}
			if len(node.Target) > 0 {
				issue.Examples = append(issue.Examples, node.Target[0])
			}
		}

		// Categorize by impact
		switch violation.Impact {
		case "critical":
			report.CriticalIssues = append(report.CriticalIssues, issue)
		case "serious":
			report.SeriousIssues = append(report.SeriousIssues, issue)
		case "moderate":
			report.HighIssues = append(report.HighIssues, issue)
		case "minor":
			report.MediumIssues = append(report.MediumIssues, issue)
		}
	}
}

// processContrastResults processes contrast check results and adds them to the report
func (d *Daemon) processContrastResults(report *PageAccessibilityReport, contrastResult *ContrastCheckResult) {
	report.ContrastSummary.TotalChecked = contrastResult.TotalElements
	report.ContrastSummary.Passed = contrastResult.PassedAA
	report.ContrastSummary.Failed = contrastResult.FailedAA

	if contrastResult.TotalElements > 0 {
		passRate := float64(contrastResult.PassedAA) / float64(contrastResult.TotalElements) * 100
		report.ContrastSummary.PassRate = fmt.Sprintf("%.1f%%", passRate)
	}

	// Extract critical failures (limit to 10)
	report.ContrastSummary.CriticalFailures = []ContrastFailure{}
	report.ContrastSummary.FailurePatterns = make(map[string]FailurePattern)

	count := 0
	for _, elem := range contrastResult.Elements {
		if !elem.PassesAA && count < 10 {
			failure := ContrastFailure{
				Selector: elem.Selector,
				Text:     elem.Text,
				Ratio:    elem.ContrastRatio,
				Required: elem.RequiredAA,
				FgColor:  elem.ForegroundColor,
				BgColor:  elem.BackgroundColor,
				Fix:      fmt.Sprintf("Increase contrast to at least %.1f:1", elem.RequiredAA),
			}
			report.ContrastSummary.CriticalFailures = append(report.ContrastSummary.CriticalFailures, failure)
			count++
		}
	}
}

// processKeyboardResults processes keyboard test results and adds them to the report
func (d *Daemon) processKeyboardResults(report *PageAccessibilityReport, keyboardResult *KeyboardTestResult) {
	report.KeyboardSummary.TotalInteractive = keyboardResult.TotalInteractive
	report.KeyboardSummary.Focusable = keyboardResult.Focusable
	report.KeyboardSummary.MissingFocusIndicator = keyboardResult.NoFocusIndicator
	report.KeyboardSummary.KeyboardTraps = keyboardResult.KeyboardTraps

	// Convert keyboard test issues to summary format
	if keyboardResult.NoFocusIndicator > 0 {
		issue := KeyboardIssue{
			Type:        "missing_focus_indicators",
			Severity:    "HIGH",
			Count:       keyboardResult.NoFocusIndicator,
			Description: fmt.Sprintf("%d elements lack visible focus indicators", keyboardResult.NoFocusIndicator),
			Fix:         "Add visible :focus styles with outline or border",
		}
		report.KeyboardSummary.Issues = append(report.KeyboardSummary.Issues, issue)
	}

	if keyboardResult.KeyboardTraps > 0 {
		issue := KeyboardIssue{
			Type:        "keyboard_traps",
			Severity:    "CRITICAL",
			Count:       keyboardResult.KeyboardTraps,
			Description: fmt.Sprintf("%d keyboard traps detected", keyboardResult.KeyboardTraps),
			Fix:         "Ensure users can navigate away from all interactive elements using keyboard",
		}
		report.KeyboardSummary.Issues = append(report.KeyboardSummary.Issues, issue)
	}
}

// calculateOverallScore calculates the overall accessibility score and compliance status
func (d *Daemon) calculateOverallScore(report *PageAccessibilityReport) {
	// Calculate score based on issues (100 - deductions)
	score := 100
	score -= len(report.CriticalIssues) * 20 // -20 per critical
	score -= len(report.SeriousIssues) * 10  // -10 per serious
	score -= len(report.HighIssues) * 5      // -5 per high
	score -= len(report.MediumIssues) * 2    // -2 per medium

	if score < 0 {
		score = 0
	}
	report.OverallScore = score

	// Determine compliance status
	if len(report.CriticalIssues) > 0 || len(report.SeriousIssues) > 0 {
		report.ComplianceStatus = "NON_COMPLIANT"
	} else if len(report.HighIssues) > 0 {
		report.ComplianceStatus = "PARTIAL"
	} else {
		report.ComplianceStatus = "COMPLIANT"
	}

	// Determine legal risk
	if len(report.CriticalIssues) > 0 {
		report.LegalRisk = "CRITICAL"
	} else if len(report.SeriousIssues) > 3 {
		report.LegalRisk = "HIGH"
	} else if len(report.SeriousIssues) > 0 || len(report.HighIssues) > 5 {
		report.LegalRisk = "MEDIUM"
	} else {
		report.LegalRisk = "LOW"
	}

	// Estimate remediation hours
	hours := len(report.CriticalIssues)*4 + len(report.SeriousIssues)*2 + len(report.HighIssues)*1
	report.EstimatedHours = hours
}

// extractWCAGCriteria extracts WCAG criteria from tags
func extractWCAGCriteria(tags []string) string {
	for _, tag := range tags {
		if strings.HasPrefix(tag, "wcag") && strings.Contains(tag, ".") {
			// Extract number like "wcag144" -> "1.4.4"
			numStr := strings.TrimPrefix(tag, "wcag")
			if len(numStr) >= 3 {
				return fmt.Sprintf("%s.%s.%s", string(numStr[0]), string(numStr[1]), numStr[2:])
			}
		}
	}
	return "Unknown"
}

// ContrastAuditResult represents a smart contrast audit with prioritized failures
type ContrastAuditResult struct {
	TotalChecked     int                       `json:"total_checked"`
	Passed           int                       `json:"passed"`
	Failed           int                       `json:"failed"`
	PassRate         string                    `json:"pass_rate"`
	CriticalFailures []ContrastFailure         `json:"critical_failures"`
	FailurePatterns  map[string]FailurePattern `json:"failure_patterns"`
}

// getContrastAudit performs a smart contrast check with prioritized failures
func (d *Daemon) getContrastAudit(tabID string, prioritySelectors []string, threshold string, timeout int) (*ContrastAuditResult, error) {
	d.debugLog("Getting contrast audit for tab: %s", tabID)

	// Run full contrast check
	contrastResult, err := d.checkContrast(tabID, "", timeout)
	if err != nil {
		return nil, fmt.Errorf("failed to check contrast: %v", err)
	}

	// Build audit result
	result := &ContrastAuditResult{
		TotalChecked:     contrastResult.TotalElements,
		Passed:           contrastResult.PassedAA,
		Failed:           contrastResult.FailedAA,
		CriticalFailures: []ContrastFailure{},
		FailurePatterns:  make(map[string]FailurePattern),
	}

	if contrastResult.TotalElements > 0 {
		passRate := float64(contrastResult.PassedAA) / float64(contrastResult.TotalElements) * 100
		result.PassRate = fmt.Sprintf("%.1f%%", passRate)
	}

	// Extract critical failures (prioritize based on selectors)
	priorityMap := make(map[string]bool)
	for _, sel := range prioritySelectors {
		priorityMap[sel] = true
	}

	// First add priority failures, then others (limit to 20 total)
	count := 0
	for _, elem := range contrastResult.Elements {
		if !elem.PassesAA && count < 20 {
			failure := ContrastFailure{
				Selector: elem.Selector,
				Text:     elem.Text,
				Ratio:    elem.ContrastRatio,
				Required: elem.RequiredAA,
				FgColor:  elem.ForegroundColor,
				BgColor:  elem.BackgroundColor,
				Fix:      fmt.Sprintf("Increase contrast to at least %.1f:1", elem.RequiredAA),
			}
			result.CriticalFailures = append(result.CriticalFailures, failure)
			count++
		}
	}

	d.debugLog("Successfully generated contrast audit for tab: %s", tabID)
	return result, nil
}

// KeyboardAuditResult represents a keyboard navigation audit
type KeyboardAuditResult struct {
	Status                string                 `json:"status"`
	TotalInteractive      int                    `json:"total_interactive"`
	Focusable             int                    `json:"focusable"`
	Issues                []KeyboardIssue        `json:"issues"`
	TabOrderIssues        []string               `json:"tab_order_issues"`
	Recommendation        string                 `json:"recommendation"`
	AccessibilityFeatures *AccessibilityFeatures `json:"accessibility_features,omitempty"`
	Confidence            *ConfidenceLevel       `json:"confidence,omitempty"`
}

// getKeyboardAudit performs a keyboard navigation assessment
func (d *Daemon) getKeyboardAudit(tabID string, checkFocusIndicators, checkTabOrder, checkKeyboardTraps, useRealKeys bool, timeout int) (*KeyboardAuditResult, error) {
	d.debugLog("Getting keyboard audit for tab: %s", tabID)

	// Always use real keyboard simulation for accurate :focus-visible detection
	keyboardResult, err := d.testKeyboardNavigationWithRealKeys(tabID, timeout)
	if err != nil {
		return nil, fmt.Errorf("failed to test keyboard navigation: %v", err)
	}

	// Build audit result
	result := &KeyboardAuditResult{
		TotalInteractive:      keyboardResult.TotalInteractive,
		Focusable:             keyboardResult.Focusable,
		Issues:                []KeyboardIssue{},
		TabOrderIssues:        []string{},
		AccessibilityFeatures: keyboardResult.AccessibilityFeatures,
		Confidence:            keyboardResult.Confidence,
	}

	// Determine status
	if keyboardResult.KeyboardTraps > 0 {
		result.Status = "FAIL"
	} else if keyboardResult.NoFocusIndicator > 0 {
		result.Status = "PARTIAL"
	} else {
		result.Status = "PASS"
	}

	// Add issues
	if checkFocusIndicators && keyboardResult.NoFocusIndicator > 0 {
		issue := KeyboardIssue{
			Type:        "missing_focus_indicators",
			Severity:    "HIGH",
			Count:       keyboardResult.NoFocusIndicator,
			Description: fmt.Sprintf("%d elements lack visible focus indicators", keyboardResult.NoFocusIndicator),
			Fix:         "Add visible :focus styles with outline or border",
		}
		result.Issues = append(result.Issues, issue)
	}

	if checkKeyboardTraps && keyboardResult.KeyboardTraps > 0 {
		issue := KeyboardIssue{
			Type:        "keyboard_traps",
			Severity:    "CRITICAL",
			Count:       keyboardResult.KeyboardTraps,
			Description: fmt.Sprintf("%d keyboard traps detected", keyboardResult.KeyboardTraps),
			Fix:         "Ensure users can navigate away from all interactive elements using keyboard",
		}
		result.Issues = append(result.Issues, issue)
	}

	// Generate recommendation
	if result.Status == "FAIL" {
		result.Recommendation = "Critical keyboard accessibility issues found. Fix keyboard traps immediately."
	} else if result.Status == "PARTIAL" {
		baseRec := "Add visible focus indicators to all interactive elements."
		// Adjust recommendation based on confidence level
		if result.Confidence != nil && result.Confidence.Level == "LOW" {
			result.Recommendation = baseRec + " " + result.Confidence.Recommendation
		} else {
			result.Recommendation = baseRec
		}
	} else {
		result.Recommendation = "Keyboard navigation is accessible."
		// Add confidence-based note even for passing results
		if result.Confidence != nil && result.Confidence.Level == "LOW" {
			result.Recommendation += " Note: " + result.Confidence.Recommendation
		}
	}

	d.debugLog("Successfully generated keyboard audit for tab: %s", tabID)
	return result, nil
}

// getFormAccessibilityAudit performs a comprehensive form accessibility check
func (d *Daemon) getFormAccessibilityAudit(tabID, formSelector string, timeout int) (*FormSummary, error) {
	d.debugLog("Getting form accessibility audit for tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, fmt.Errorf("failed to get page: %v", err)
	}

	// Determine the selector to use
	selector := formSelector
	if selector == "" {
		selector = "form"
	}

	// JavaScript to analyze forms
	// Note: page.Eval() expects an arrow function, not an IIFE
	jsCode := `() => {
		try {
			const forms = document.querySelectorAll('` + selector + `');
			const result = {
				forms_found: forms.length,
				forms: []
			};

			forms.forEach((form, index) => {
				const formData = {
					id: form.id || 'form-' + index,
					fields: form.querySelectorAll('input, select, textarea').length,
					issues: [],
					aria_compliance: 'FULL',
					keyboard_accessible: true,
					required_fields_marked: true
				};

				// Check for labels
				const inputs = form.querySelectorAll('input:not([type="hidden"]), select, textarea');
				let missingLabels = 0;
				inputs.forEach(input => {
					const id = input.id;
					if (id) {
						const label = form.querySelector('label[for="' + id + '"]');
						if (!label && !input.getAttribute('aria-label') && !input.getAttribute('aria-labelledby')) {
							missingLabels++;
						}
					}
				});

				if (missingLabels > 0) {
					formData.issues.push({
						type: 'missing_labels',
						severity: 'SERIOUS',
						count: missingLabels,
						description: missingLabels + ' fields lack proper labels',
						fix: 'Add <label> elements or aria-label attributes'
					});
					formData.aria_compliance = 'PARTIAL';
				}

				// Check submit button contrast (simplified)
				const submitBtn = form.querySelector('button[type="submit"], input[type="submit"]');
				if (submitBtn) {
					const styles = window.getComputedStyle(submitBtn);
					// Note: Actual contrast calculation would be more complex
					formData.issues.push({
						type: 'submit_button_check',
						severity: 'INFO',
						description: 'Submit button found - verify contrast manually',
						fix: 'Ensure submit button has 4.5:1 contrast ratio'
					});
				}

				result.forms.push(formData);
			});

			return result;
		} catch (error) {
			return {
				error: error.message,
				forms_found: 0,
				forms: []
			};
		}
	}`

	// Execute JavaScript with timeout
	var resultData interface{}
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			result *proto.RuntimeRemoteObject
			err    error
		}, 1)

		go func() {
			res, err := page.Eval(jsCode)
			done <- struct {
				result *proto.RuntimeRemoteObject
				err    error
			}{res, err}
		}()

		select {
		case <-ctx.Done():
			return nil, fmt.Errorf("form analysis timed out after %d seconds", timeout)
		case result := <-done:
			if result.err != nil {
				return nil, fmt.Errorf("failed to analyze forms: %v", result.err)
			}
			if result.result == nil {
				return nil, fmt.Errorf("form analysis returned nil result")
			}
			// Convert the result to JSON
			jsonBytes, err := json.Marshal(result.result.Value.Val())
			if err != nil {
				return nil, fmt.Errorf("failed to marshal result value: %v (value type: %T, value: %v)", err, result.result.Value.Val(), result.result.Value.Val())
			}
			if err := json.Unmarshal(jsonBytes, &resultData); err != nil {
				return nil, fmt.Errorf("failed to parse form analysis result: %v (json: %s)", err, string(jsonBytes))
			}
		}
	} else {
		res, err := page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to analyze forms: %v", err)
		}
		if res == nil {
			return nil, fmt.Errorf("form analysis returned nil result")
		}
		// Convert the result to JSON
		jsonBytes, err := json.Marshal(res.Value.Val())
		if err != nil {
			return nil, fmt.Errorf("failed to marshal result value: %v (value type: %T, value: %v)", err, res.Value.Val(), res.Value.Val())
		}
		if err := json.Unmarshal(jsonBytes, &resultData); err != nil {
			return nil, fmt.Errorf("failed to parse form analysis result: %v (json: %s)", err, string(jsonBytes))
		}
	}

	// Convert to FormSummary
	var summary FormSummary
	dataBytes, err := json.Marshal(resultData)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal form data: %v", err)
	}
	if err := json.Unmarshal(dataBytes, &summary); err != nil {
		return nil, fmt.Errorf("failed to unmarshal form summary: %v", err)
	}

	d.debugLog("Successfully generated form accessibility audit for tab: %s (found %d forms)", tabID, summary.FormsFound)
	return &summary, nil
}

// DiviStructure represents the extracted Divi page structure
type DiviStructure struct {
	URL      string        `json:"url"`
	Sections []DiviSection `json:"sections"`
	Metadata struct {
		ExtractionDate string `json:"extraction_date"`
		Accuracy       string `json:"accuracy"`
		Limitations    string `json:"limitations"`
	} `json:"metadata"`
}

// DiviSection represents a Divi section
type DiviSection struct {
	Type            string    `json:"type"` // regular, specialty, fullwidth
	HasParallax     bool      `json:"has_parallax"`
	BackgroundColor string    `json:"background_color"`
	BackgroundImage string    `json:"background_image"`
	BackgroundStyle string    `json:"background_style"`
	Rows            []DiviRow `json:"rows"`
	CSSClasses      []string  `json:"css_classes"`
}

// DiviRow represents a Divi row
type DiviRow struct {
	ColumnStructure string       `json:"column_structure"` // e.g., "1_2,1_2" for two half columns
	Columns         []DiviColumn `json:"columns"`
	CSSClasses      []string     `json:"css_classes"`
}

// DiviColumn represents a Divi column
type DiviColumn struct {
	Type       string       `json:"type"` // e.g., "1_2", "1_3", "4_4"
	Modules    []DiviModule `json:"modules"`
	CSSClasses []string     `json:"css_classes"`
}

// DiviModule represents a Divi module
type DiviModule struct {
	Type       string            `json:"type"` // text, image, button, blurb, etc.
	Content    string            `json:"content"`
	Attributes map[string]string `json:"attributes"`
	CSSClasses []string          `json:"css_classes"`
}

// DiviImage represents an extracted image
type DiviImage struct {
	URL          string `json:"url"`
	Alt          string `json:"alt"`
	Title        string `json:"title"`
	Width        int    `json:"width"`
	Height       int    `json:"height"`
	Context      string `json:"context"` // e.g., "section 0, row 0, column 0, module 2"
	IsBackground bool   `json:"is_background"`
}

// DiviContent represents extracted content
type DiviContent struct {
	URL      string       `json:"url"`
	Modules  []DiviModule `json:"modules"`
	Images   []DiviImage  `json:"images"`
	Metadata struct {
		ExtractionDate string `json:"extraction_date"`
		TotalModules   int    `json:"total_modules"`
		TotalImages    int    `json:"total_images"`
	} `json:"metadata"`
}

// extractDiviStructure extracts Divi page structure from rendered HTML
func (d *Daemon) extractDiviStructure(tabID string, timeout int) (*DiviStructure, error) {
	d.debugLog("Extracting Divi structure from tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// JavaScript to extract Divi structure
	// Note: page.Eval expects a function expression, not an IIFE
	jsCode := `() => {
		const result = {
			url: window.location.href,
			sections: [],
			metadata: {
				extraction_date: new Date().toISOString(),
				accuracy: "60-70% (approximation from CSS classes)",
				limitations: "Cannot access original Divi shortcode/JSON or builder settings"
			}
		};

		// Find all Divi sections
		const sections = document.querySelectorAll('.et_pb_section, .et_section_specialty, .et_pb_fullwidth_section');

			sections.forEach((section, sectionIndex) => {
				const sectionData = {
					type: 'regular',
					has_parallax: false,
					background_color: '',
					background_image: '',
					background_style: '',
					rows: [],
					css_classes: Array.from(section.classList)
				};

				// Determine section type from CSS classes
				if (section.classList.contains('et_section_specialty')) {
					sectionData.type = 'specialty';
				} else if (section.classList.contains('et_pb_fullwidth_section')) {
					sectionData.type = 'fullwidth';
				}

				// Check for parallax
				if (section.classList.contains('et_pb_section_parallax')) {
					sectionData.has_parallax = true;
				}

				// Get computed styles
				const styles = window.getComputedStyle(section);
				sectionData.background_color = styles.backgroundColor;
				sectionData.background_image = styles.backgroundImage;

				// Extract background style
				if (styles.backgroundImage && styles.backgroundImage !== 'none') {
					if (styles.backgroundImage.includes('gradient')) {
						sectionData.background_style = 'gradient';
					} else {
						sectionData.background_style = 'image';
					}
				}

				// Find all rows in this section
				const rows = section.querySelectorAll('.et_pb_row, .et_pb_row_inner');

				rows.forEach((row, rowIndex) => {
					const rowData = {
						column_structure: '',
						columns: [],
						css_classes: Array.from(row.classList)
					};

					// Find all columns in this row
					const columns = row.querySelectorAll('.et_pb_column');
					const columnTypes = [];

					columns.forEach((column, columnIndex) => {
						const columnData = {
							type: '',
							modules: [],
							css_classes: Array.from(column.classList)
						};

						// Determine column type from CSS classes
						const columnClass = Array.from(column.classList).find(cls => cls.startsWith('et_pb_column_'));
						if (columnClass) {
							columnData.type = columnClass.replace('et_pb_column_', '');
							columnTypes.push(columnData.type);
						}

						// Find all modules in this column
						const modules = column.querySelectorAll('[class*="et_pb_module"]');

						modules.forEach((module, moduleIndex) => {
							const moduleData = {
								type: 'unknown',
								content: '',
								attributes: {},
								css_classes: Array.from(module.classList)
							};

							// Determine module type from CSS classes
							if (module.classList.contains('et_pb_text')) {
								moduleData.type = 'text';
								moduleData.content = module.innerHTML;
							} else if (module.classList.contains('et_pb_image')) {
								moduleData.type = 'image';
								const img = module.querySelector('img');
								if (img) {
									moduleData.attributes.src = img.src;
									moduleData.attributes.alt = img.alt || '';
								}
							} else if (module.classList.contains('et_pb_button')) {
								moduleData.type = 'button';
								const btn = module.querySelector('a');
								if (btn) {
									moduleData.content = btn.textContent;
									moduleData.attributes.href = btn.href;
								}
							} else if (module.classList.contains('et_pb_blurb')) {
								moduleData.type = 'blurb';
								moduleData.content = module.innerHTML;
							} else if (module.classList.contains('et_pb_cta')) {
								moduleData.type = 'cta';
								moduleData.content = module.innerHTML;
							} else if (module.classList.contains('et_pb_slider')) {
								moduleData.type = 'slider';
							} else if (module.classList.contains('et_pb_gallery')) {
								moduleData.type = 'gallery';
							} else if (module.classList.contains('et_pb_video')) {
								moduleData.type = 'video';
							}

							columnData.modules.push(moduleData);
						});

						rowData.columns.push(columnData);
					});

					// Build column structure string
					rowData.column_structure = columnTypes.join(',');

					sectionData.rows.push(rowData);
				});

				result.sections.push(sectionData);
			});

			return result;
		}`

	// Execute JavaScript with timeout
	var resultData interface{}
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			data interface{}
			err  error
		}, 1)

		go func() {
			data, err := page.Eval(jsCode)
			var value interface{}
			if data != nil {
				value = data.Value
			}
			done <- struct {
				data interface{}
				err  error
			}{value, err}
		}()

		select {
		case result := <-done:
			if result.err != nil {
				return nil, fmt.Errorf("failed to execute JavaScript: %v", result.err)
			}
			resultData = result.data
		case <-ctx.Done():
			return nil, fmt.Errorf("timeout waiting for JavaScript execution")
		}
	} else {
		data, err := page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
		}
		resultData = data.Value
	}

	// Convert result to DiviStructure
	var structure DiviStructure
	dataBytes, err := json.Marshal(resultData)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal result data: %v", err)
	}
	if err := json.Unmarshal(dataBytes, &structure); err != nil {
		return nil, fmt.Errorf("failed to unmarshal Divi structure: %v", err)
	}

	d.debugLog("Successfully extracted Divi structure from tab: %s (found %d sections)", tabID, len(structure.Sections))
	return &structure, nil
}

// extractDiviImages extracts all images from a Divi page
func (d *Daemon) extractDiviImages(tabID string, timeout int) ([]DiviImage, error) {
	d.debugLog("Extracting Divi images from tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// JavaScript to extract images
	// Note: page.Eval expects a function expression, not an IIFE
	jsCode := `() => {
		const images = [];
		let imageIndex = 0;

		// Extract regular images
		document.querySelectorAll('img').forEach((img) => {
			if (img.src && img.src !== '') {
				images.push({
					url: img.src,
					alt: img.alt || '',
					title: img.title || '',
					width: img.naturalWidth || img.width || 0,
					height: img.naturalHeight || img.height || 0,
					context: 'image ' + imageIndex,
					is_background: false
				});
				imageIndex++;
			}
		});

		// Extract background images
		document.querySelectorAll('*').forEach((element) => {
			const styles = window.getComputedStyle(element);
			const bgImage = styles.backgroundImage;

			if (bgImage && bgImage !== 'none' && !bgImage.includes('gradient')) {
				// Extract URL from background-image
				const urlMatch = bgImage.match(/url\(['"]?([^'"]+)['"]?\)/);
				if (urlMatch && urlMatch[1]) {
					images.push({
						url: urlMatch[1],
						alt: '',
						title: '',
						width: 0,
						height: 0,
						context: 'background ' + imageIndex,
						is_background: true
					});
					imageIndex++;
				}
			}
		});

		return images;
	}`

	// Execute JavaScript with timeout
	var resultData interface{}
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			data interface{}
			err  error
		}, 1)

		go func() {
			data, err := page.Eval(jsCode)
			var value interface{}
			if data != nil {
				value = data.Value
			}
			done <- struct {
				data interface{}
				err  error
			}{value, err}
		}()

		select {
		case result := <-done:
			if result.err != nil {
				return nil, fmt.Errorf("failed to execute JavaScript: %v", result.err)
			}
			resultData = result.data
		case <-ctx.Done():
			return nil, fmt.Errorf("timeout waiting for JavaScript execution")
		}
	} else {
		data, err := page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
		}
		resultData = data.Value
	}

	// Convert result to []DiviImage
	var images []DiviImage
	dataBytes, err := json.Marshal(resultData)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal result data: %v", err)
	}
	if err := json.Unmarshal(dataBytes, &images); err != nil {
		return nil, fmt.Errorf("failed to unmarshal Divi images: %v", err)
	}

	d.debugLog("Successfully extracted Divi images from tab: %s (found %d images)", tabID, len(images))
	return images, nil
}

// extractDiviContent extracts text content and module data from Divi pages
func (d *Daemon) extractDiviContent(tabID string, timeout int) (*DiviContent, error) {
	d.debugLog("Extracting Divi content from tab: %s", tabID)

	page, err := d.getTab(tabID)
	if err != nil {
		return nil, err
	}

	// JavaScript to extract content
	// Note: page.Eval expects a function expression, not an IIFE
	jsCode := `() => {
		const result = {
			url: window.location.href,
			modules: [],
			images: [],
			metadata: {
				extraction_date: new Date().toISOString(),
				total_modules: 0,
				total_images: 0
			}
		};

		// Extract all Divi modules
		const modules = document.querySelectorAll('[class*="et_pb_module"]');

			modules.forEach((module, index) => {
				const moduleData = {
					type: 'unknown',
					content: '',
					attributes: {},
					css_classes: Array.from(module.classList)
				};

				// Determine module type and extract content
				if (module.classList.contains('et_pb_text')) {
					moduleData.type = 'text';
					moduleData.content = module.innerHTML;
				} else if (module.classList.contains('et_pb_image')) {
					moduleData.type = 'image';
					const img = module.querySelector('img');
					if (img) {
						moduleData.attributes.src = img.src;
						moduleData.attributes.alt = img.alt || '';
						moduleData.attributes.width = img.width.toString();
						moduleData.attributes.height = img.height.toString();
					}
				} else if (module.classList.contains('et_pb_button')) {
					moduleData.type = 'button';
					const btn = module.querySelector('a');
					if (btn) {
						moduleData.content = btn.textContent;
						moduleData.attributes.href = btn.href;
						moduleData.attributes.target = btn.target || '';
					}
				} else if (module.classList.contains('et_pb_blurb')) {
					moduleData.type = 'blurb';
					const title = module.querySelector('.et_pb_blurb_content h4');
					const content = module.querySelector('.et_pb_blurb_content');
					if (title) moduleData.attributes.title = title.textContent;
					if (content) moduleData.content = content.innerHTML;
				} else if (module.classList.contains('et_pb_cta')) {
					moduleData.type = 'cta';
					const title = module.querySelector('.et_pb_cta_title');
					const content = module.querySelector('.et_pb_cta_content');
					if (title) moduleData.attributes.title = title.textContent;
					if (content) moduleData.content = content.innerHTML;
				}

				result.modules.push(moduleData);
			});

			// Extract images
			document.querySelectorAll('img').forEach((img, index) => {
				if (img.src && img.src !== '') {
					result.images.push({
						url: img.src,
						alt: img.alt || '',
						title: img.title || '',
						width: img.naturalWidth || img.width || 0,
						height: img.naturalHeight || img.height || 0,
						context: 'image ' + index,
						is_background: false
					});
				}
			});

			result.metadata.total_modules = result.modules.length;
			result.metadata.total_images = result.images.length;

			return result;
		}`

	// Execute JavaScript with timeout
	var resultData interface{}
	if timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
		defer cancel()

		done := make(chan struct {
			data interface{}
			err  error
		}, 1)

		go func() {
			data, err := page.Eval(jsCode)
			var value interface{}
			if data != nil {
				value = data.Value
			}
			done <- struct {
				data interface{}
				err  error
			}{value, err}
		}()

		select {
		case result := <-done:
			if result.err != nil {
				return nil, fmt.Errorf("failed to execute JavaScript: %v", result.err)
			}
			resultData = result.data
		case <-ctx.Done():
			return nil, fmt.Errorf("timeout waiting for JavaScript execution")
		}
	} else {
		data, err := page.Eval(jsCode)
		if err != nil {
			return nil, fmt.Errorf("failed to execute JavaScript: %v", err)
		}
		resultData = data.Value
	}

	// Convert result to DiviContent
	var content DiviContent
	dataBytes, err := json.Marshal(resultData)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal result data: %v", err)
	}
	if err := json.Unmarshal(dataBytes, &content); err != nil {
		return nil, fmt.Errorf("failed to unmarshal Divi content: %v", err)
	}

	d.debugLog("Successfully extracted Divi content from tab: %s (found %d modules, %d images)", tabID, content.Metadata.TotalModules, content.Metadata.TotalImages)
	return &content, nil
}
