//go:build !windows && linux

package infra

import (
	"os/exec"
	"strconv"
	"strings"
	"testing"
)

// newCatWindow crea una window detached en <session> que corre `cat` (un
// proceso persistente que simula un claude parkeado) y devuelve su window_id.
func newCatWindow(t *testing.T, socket, session string) string {
	t.Helper()
	out, err := exec.Command("tmux", "-L", socket,
		"new-window", "-d", "-P", "-F", "#{window_id}", "-t", session, "cat").CombinedOutput()
	if err != nil {
		t.Fatalf("new-window cat: %v (%s)", err, out)
	}
	id := strings.TrimSpace(string(out))
	if id == "" {
		t.Fatal("new-window cat no devolvio window_id")
	}
	return id
}

// consolePanes devuelve las lineas "pane_index pane_id width" de la window
// console de <session>.
func consolePanes(t *testing.T, socket, session string) []string {
	t.Helper()
	out, err := exec.Command("tmux", "-L", socket,
		"list-panes", "-t", session+":console",
		"-F", "#{pane_index} #{pane_id} #{pane_width}").CombinedOutput()
	if err != nil {
		t.Fatalf("list-panes console: %v (%s)", err, out)
	}
	var lines []string
	for _, l := range strings.Split(strings.TrimSpace(string(out)), "\n") {
		if l = strings.TrimSpace(l); l != "" {
			lines = append(lines, l)
		}
	}
	return lines
}

func TestTmuxSwapWindowIntoConsole(t *testing.T) {
	tmuxAvailable(t)
	socket := isolatedSocket(t)
	session := "fleet"
	startConsoleSession(t, socket, session)

	// Una window aparte con `cat` simula un Claude parkeado conmutable.
	claudeWin := newCatWindow(t, socket, session)

	// Estado inicial: console tiene un solo pane (la TUI, indice 0).
	if got := len(consolePanes(t, socket, session)); got != 1 {
		t.Fatalf("console deberia empezar con 1 pane, tiene %d", got)
	}

	if err := TmuxSwapWindowIntoConsole(socket, session, claudeWin); err != nil {
		t.Fatalf("TmuxSwapWindowIntoConsole: %v", err)
	}

	// Tras el swap: console tiene 2 panes y el sidebar (pane de MENOR indice)
	// mide 52 columnas (default del sidebar, ya que la console arrancó con un
	// solo pane full-width). Se localiza por menor pane_index, NO por indice
	// literal "0": bajo `pane-base-index 1` (que el socket hereda de
	// ~/.tmux.conf) el primer pane es el indice 1.
	panes := consolePanes(t, socket, session)
	if len(panes) != 2 {
		t.Fatalf("console deberia tener 2 panes tras swap, tiene %d: %v", len(panes), panes)
	}
	minIdx, sidebarWidth, found := -1, 0, false
	for _, line := range panes {
		f := strings.Fields(line)
		if len(f) < 3 {
			continue
		}
		idx, err := strconv.Atoi(f[0])
		if err != nil {
			t.Fatalf("pane_index no numerico: %q", f[0])
		}
		w, err := strconv.Atoi(f[2])
		if err != nil {
			t.Fatalf("ancho de pane no numerico: %q", f[2])
		}
		if !found || idx < minIdx {
			minIdx, sidebarWidth, found = idx, w, true
		}
	}
	if !found {
		t.Fatal("no se encontro ningun pane en console")
	}
	if sidebarWidth != 52 {
		t.Errorf("ancho del sidebar (pane menor indice = %d) = %d, esperado 52", minIdx, sidebarWidth)
	}
}

func TestTmuxSwapWindowIntoConsoleParksPrevious(t *testing.T) {
	tmuxAvailable(t)
	socket := isolatedSocket(t)
	session := "fleet"
	startConsoleSession(t, socket, session)

	winA := newCatWindow(t, socket, session)
	winB := newCatWindow(t, socket, session)

	// Trae A a console.
	if err := TmuxSwapWindowIntoConsole(socket, session, winA); err != nil {
		t.Fatalf("swap A: %v", err)
	}
	if got := len(consolePanes(t, socket, session)); got != 2 {
		t.Fatalf("tras swap A console deberia tener 2 panes, tiene %d", got)
	}

	// Trae B: A se parkea fuera, console vuelve a 2 panes (TUI + B).
	if err := TmuxSwapWindowIntoConsole(socket, session, winB); err != nil {
		t.Fatalf("swap B: %v", err)
	}
	if got := len(consolePanes(t, socket, session)); got != 2 {
		t.Fatalf("tras swap B console deberia tener 2 panes, tiene %d", got)
	}

	// El Claude A parkeado debe seguir vivo en alguna window de la sesion.
	out, err := exec.Command("tmux", "-L", socket,
		"list-windows", "-t", session, "-F", "#{window_id}").CombinedOutput()
	if err != nil {
		t.Fatalf("list-windows: %v (%s)", err, out)
	}
	winCount := 0
	for _, l := range strings.Split(strings.TrimSpace(string(out)), "\n") {
		if strings.TrimSpace(l) != "" {
			winCount++
		}
	}
	// Esperadas: console + (window de A parkeada). winB se consumio al unir su
	// pane a console (la window vacia se cierra). winA: una window de parking.
	if winCount < 2 {
		t.Errorf("se esperaban >=2 windows (console + A parkeado), hay %d", winCount)
	}
}

func TestTmuxSwapWindowIntoConsoleEmptyArgs(t *testing.T) {
	if err := TmuxSwapWindowIntoConsole("", "fleet", "@1"); err == nil {
		t.Error("socket vacio deberia dar error")
	}
	if err := TmuxSwapWindowIntoConsole("sock", "", "@1"); err == nil {
		t.Error("session vacia deberia dar error")
	}
	if err := TmuxSwapWindowIntoConsole("sock", "fleet", ""); err == nil {
		t.Error("windowID vacio deberia dar error")
	}
}
