package browser

import (
	"fmt"
	"math/rand"
	"strings"
)

// CdpClickHuman hace click en el elemento identificado por selector CSS con
// movimiento humano: obtiene el bbox, calcula un punto destino ligeramente
// desplazado del centro, mueve el ratón por una trayectoria de Bézier cúbica
// y luego despacha mousePressed/mouseReleased con una micro-pausa entre ellos.
//
// opts controla la trayectoria del movimiento previo al click.
// Para configurar el origen del movimiento usa opts.FromX / opts.FromY.
func CdpClickHuman(c *CDPConn, selector string, opts MouseHumanOpts) error {
	if c == nil {
		return fmt.Errorf("cdp click human: conexion nula")
	}

	// Obtener bounding box del selector
	js := fmt.Sprintf(`(function() {
		var el = document.querySelector(%q);
		if (!el) return null;
		var r = el.getBoundingClientRect();
		return JSON.stringify({x: r.left, y: r.top, w: r.width, h: r.height});
	})()`, selector)

	bboxStr, err := CdpEvaluate(c, js)
	if err != nil {
		return fmt.Errorf("cdp click human: obtener bbox de %q: %w", selector, err)
	}
	if bboxStr == "" || bboxStr == "null" {
		return fmt.Errorf("cdp click human: elemento %q no encontrado en el DOM", selector)
	}

	bboxStr = strings.Trim(bboxStr, `"`)
	bx, by, bw, bh, err := parseBbox(bboxStr)
	if err != nil {
		return fmt.Errorf("cdp click human: parsear bbox %q: %w", bboxStr, err)
	}

	// Scroll al elemento para que sea visible
	scrollJS := fmt.Sprintf(`document.querySelector(%q).scrollIntoView({block:'center'})`, selector)
	if _, err := CdpEvaluate(c, scrollJS); err != nil {
		_ = err // no fatal
	}

	// Punto destino: centro + pequeño offset aleatorio (±15% del tamaño)
	offX := (rand.Float64()*2 - 1) * bw * 0.15
	offY := (rand.Float64()*2 - 1) * bh * 0.15
	toX := bx + bw/2 + offX
	toY := by + bh/2 + offY

	// Delegar en el primitivo compartido: mueve el ratón con trayectoria humana
	// y despacha press/release con micro-pausa.
	if err := CdpClickXYHuman(c, toX, toY, opts); err != nil {
		return fmt.Errorf("cdp click human: %w", err)
	}

	return nil
}

// parseBbox extrae left, top, width, height de un JSON como {"x":10,"y":20,"w":100,"h":40}.
func parseBbox(s string) (left, top, width, height float64, err error) {
	// Reutiliza el mismo parser manual que parseCoords para evitar encoding/json
	s = strings.TrimSpace(s)
	s = strings.TrimPrefix(s, "{")
	s = strings.TrimSuffix(s, "}")

	for part := range strings.SplitSeq(s, ",") {
		kv := strings.SplitN(strings.TrimSpace(part), ":", 2)
		if len(kv) != 2 {
			continue
		}
		k := strings.Trim(strings.TrimSpace(kv[0]), `"`)
		var v float64
		if _, e := fmt.Sscanf(strings.TrimSpace(kv[1]), "%f", &v); e != nil {
			err = fmt.Errorf("parsear valor %q: %w", kv[1], e)
			return
		}
		switch k {
		case "x":
			left = v
		case "y":
			top = v
		case "w":
			width = v
		case "h":
			height = v
		}
	}
	return
}
