// Tests e2e contra kanban server (puerto 8095) usando funciones del registry.
// Requiere kanban backend corriendo + Chrome accesible (WSL2 o Linux).
//
// Ejecucion:
//   cd e2e && go test -v -tags fts5 ./...
//   o:    BASE_URL=http://localhost:5180 go test -v ./...   (modo dev con Vite)
//
// Variables de entorno:
//   BASE_URL    — default http://localhost:8095
//   KANBAN_USER — default e2e
//   KANBAN_PASS — default e2etest
//   HEADLESS    — "1" para headless. Default "1"
//
// Reusa funciones del registry: chrome_launch, cdp_*. NO duplica logica.
package e2e

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"testing"
	"time"

	"fn-registry/functions/browser"
)

const cdpPort = 9335

type ctx struct {
	t       *testing.T
	conn    *browser.CDPConn
	chromePID int
	baseURL string
}

func envOr(k, dflt string) string {
	if v := os.Getenv(k); v != "" {
		return v
	}
	return dflt
}

func setup(t *testing.T) *ctx {
	t.Helper()
	baseURL := envOr("BASE_URL", "http://localhost:8095")

	// Verificar que el backend responde antes de lanzar Chrome.
	resp, err := http.Get(baseURL + "/api/board")
	if err != nil {
		t.Skipf("backend no accesible en %s: %v", baseURL, err)
	}
	resp.Body.Close()

	headless := envOr("HEADLESS", "1") == "1"
	pid, err := browser.ChromeLaunch(browser.ChromeLaunchOpts{
		Port:        cdpPort,
		UserDataDir: "/tmp/kanban-e2e-profile",
		Headless:    headless,
	})
	if err != nil {
		t.Skipf("chrome_launch fallo: %v", err)
	}

	conn, err := browser.CdpConnect(cdpPort)
	if err != nil {
		_ = browser.CdpClose(nil, pid)
		t.Fatalf("cdp_connect fallo: %v", err)
	}

	c := &ctx{t: t, conn: conn, chromePID: pid, baseURL: baseURL}
	t.Cleanup(func() { _ = browser.CdpClose(c.conn, c.chromePID) })
	return c
}

func (c *ctx) navigate(path string) {
	c.t.Helper()
	if err := browser.CdpNavigate(c.conn, c.baseURL+path); err != nil {
		c.t.Fatalf("navigate %s: %v", path, err)
	}
	if err := browser.CdpWaitLoad(c.conn, 10*time.Second); err != nil {
		c.t.Fatalf("wait_load %s: %v", path, err)
	}
}

func (c *ctx) screenshot(name string) {
	c.t.Helper()
	dir := "screenshots"
	_ = os.MkdirAll(dir, 0o755)
	out := fmt.Sprintf("%s/%s.png", dir, name)
	if err := browser.CdpScreenshot(c.conn, out, browser.CdpScreenshotOpts{Format: "png"}); err != nil {
		c.t.Logf("screenshot fallo (%s): %v", name, err)
		return
	}
	c.t.Logf("screenshot: %s", out)
}

func (c *ctx) eval(expr string) string {
	c.t.Helper()
	out, err := browser.CdpEvaluate(c.conn, expr)
	if err != nil {
		c.t.Fatalf("eval (%s): %v", expr, err)
	}
	return out
}

// --- Tests ---

func TestE2E_HomeLoads(t *testing.T) {
	c := setup(t)
	c.navigate("/")

	// Login form o board (segun haya sesion previa). Busca cualquier rasgo visible.
	if err := browser.CdpWaitElement(c.conn, "body", 5*time.Second); err != nil {
		t.Fatalf("body no aparecio: %v", err)
	}
	html, err := browser.CdpGetHTML(c.conn)
	if err != nil {
		t.Fatalf("get_html: %v", err)
	}
	if !strings.Contains(strings.ToLower(html), "kanban") &&
		!strings.Contains(strings.ToLower(html), "iniciar") &&
		!strings.Contains(strings.ToLower(html), "login") {
		t.Errorf("home no contiene rastros esperados (kanban/login). HTML[:200]=%s", html[:min(len(html), 200)])
	}
	c.screenshot("01_home")
}

func TestE2E_ApiBoardResponds(t *testing.T) {
	baseURL := envOr("BASE_URL", "http://localhost:8095")
	resp, err := http.Get(baseURL + "/api/board")
	if err != nil {
		t.Skipf("backend no accesible: %v", err)
	}
	defer resp.Body.Close()
	// 401 (sin sesion) o 200 (sesion activa) — ambos validos.
	if resp.StatusCode != 200 && resp.StatusCode != 401 {
		t.Errorf("/api/board status inesperado: %d", resp.StatusCode)
	}
}

func TestE2E_FlagsEndpoint_DoesNotExist(t *testing.T) {
	// Smoke: endpoint /api/me devuelve 401 sin auth (no 5xx).
	baseURL := envOr("BASE_URL", "http://localhost:8095")
	resp, err := http.Get(baseURL + "/api/me")
	if err != nil {
		t.Skipf("backend no accesible: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 500 {
		t.Errorf("/api/me devolvio 5xx: %d", resp.StatusCode)
	}
}

func TestE2E_FrontendBundleHasNoConsoleErrors(t *testing.T) {
	c := setup(t)
	c.navigate("/")
	if err := browser.CdpWaitElement(c.conn, "body", 5*time.Second); err != nil {
		t.Fatalf("body: %v", err)
	}
	// Comprueba que no hay errores graves en el DOM.
	val := c.eval(`document.querySelectorAll('script[src*="error"]').length`)
	if !strings.Contains(val, "0") {
		t.Errorf("scripts de error detectados: %s", val)
	}
	// Verifica que el bundle se cargo (algun script de assets).
	val = c.eval(`document.querySelectorAll('script[src*="/assets/"]').length`)
	if strings.Contains(val, "0") {
		t.Errorf("bundle no cargado: %s", val)
	}
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
