package browser

import (
	"encoding/json"
	"fmt"
)

// CdpEvaluate ejecuta una expresion JavaScript en la pagina actual via Runtime.evaluate.
// Retorna el resultado como string. Soporta expresiones simples y complejas.
// Para valores no-string, Chrome los serializa a JSON.
func CdpEvaluate(c *CDPConn, expression string) (string, error) {
	if c == nil {
		return "", fmt.Errorf("cdp evaluate: conexion nula")
	}

	params := map[string]any{
		"expression":            expression,
		"returnByValue":         true,
		"awaitPromise":          true,
		"userGesture":           true,
	}

	result, err := c.sendCDP("Runtime.evaluate", params)
	if err != nil {
		return "", fmt.Errorf("cdp evaluate: %w", err)
	}

	// Verificar excepcion JS
	if exc, ok := result["exceptionDetails"]; ok && exc != nil {
		excMap, _ := exc.(map[string]any)
		text, _ := excMap["text"].(string)
		return "", fmt.Errorf("cdp evaluate: excepcion JS: %s", text)
	}

	// Extraer valor del resultado
	resVal, ok := result["result"].(map[string]any)
	if !ok {
		return "", fmt.Errorf("cdp evaluate: resultado inesperado: %v", result)
	}

	value, ok := resVal["value"]
	if !ok {
		// Puede ser undefined o un tipo no serializable
		typ, _ := resVal["type"].(string)
		return typ, nil
	}

	// Strings se devuelven tal cual (sin comillas). Objetos y arrays JS, que Chrome
	// deserializa a map/slice cuando returnByValue=true, se serializan a JSON real
	// en vez de la repr de Go de fmt.Sprintf("%v") (que produciria "map[a:1]" en lugar
	// de {"a":1}). Asi el caller puede parsear datos estructurados.
	if s, ok := value.(string); ok {
		return s, nil
	}
	b, err := json.Marshal(value)
	if err != nil {
		return fmt.Sprintf("%v", value), nil
	}
	return string(b), nil
}
