package main

import (
	"os/exec"
	"testing"
	"time"
)

func TestNewPool(t *testing.T) {
	p := NewPool()
	if p == nil {
		t.Fatal("NewPool returned nil")
	}
}

func TestGetEmpty(t *testing.T) {
	p := NewPool()
	s := p.Get("nonexistent")
	if s != nil {
		t.Fatal("Get on empty pool should return nil")
	}
}

func TestIsAliveEmpty(t *testing.T) {
	p := NewPool()
	if p.IsAlive("nonexistent") {
		t.Fatal("IsAlive on empty pool should return false")
	}
}

// setupTmuxSession creates a tmux session for testing. Returns a cleanup function.
func setupTmuxSession(t *testing.T, name string) func() {
	t.Helper()
	// Kill any leftover session with this name
	exec.Command("tmux", "kill-session", "-t", name).Run()
	cmd := exec.Command("tmux", "new-session", "-d", "-s", name, "-x", "80", "-y", "24")
	if err := cmd.Run(); err != nil {
		t.Fatalf("failed to create tmux session %q: %v", name, err)
	}
	return func() {
		exec.Command("tmux", "kill-session", "-t", name).Run()
	}
}

func TestAttachCreatesSession(t *testing.T) {
	name := "test-pool-attach"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()
	defer p.CloseAll()

	sess, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Attach failed: %v", err)
	}
	if sess == nil {
		t.Fatal("Attach returned nil session")
	}
	if sess.PTY == nil {
		t.Fatal("Session PTY is nil")
	}
	if sess.Cmd == nil {
		t.Fatal("Session Cmd is nil")
	}
	if sess.Cols != 80 {
		t.Errorf("Cols = %d, want 80", sess.Cols)
	}
	if sess.Rows != 24 {
		t.Errorf("Rows = %d, want 24", sess.Rows)
	}
}

func TestGetReturnsSession(t *testing.T) {
	name := "test-pool-get"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()
	defer p.CloseAll()

	_, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Attach failed: %v", err)
	}

	sess := p.Get(name)
	if sess == nil {
		t.Fatal("Get returned nil after Attach")
	}
}

func TestIsAliveAfterAttach(t *testing.T) {
	name := "test-pool-alive"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()
	defer p.CloseAll()

	_, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Attach failed: %v", err)
	}

	if !p.IsAlive(name) {
		t.Fatal("IsAlive should return true after Attach")
	}
}

func TestAttachReusesLiveSession(t *testing.T) {
	name := "test-pool-reuse"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()
	defer p.CloseAll()

	sess1, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("First Attach failed: %v", err)
	}

	sess2, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Second Attach failed: %v", err)
	}

	if sess1 != sess2 {
		t.Fatal("Attach should reuse existing live session")
	}
}

func TestResize(t *testing.T) {
	name := "test-pool-resize"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()
	defer p.CloseAll()

	_, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Attach failed: %v", err)
	}

	err = p.Resize(name, 120, 40)
	if err != nil {
		t.Fatalf("Resize failed: %v", err)
	}

	sess := p.Get(name)
	if sess.Cols != 120 {
		t.Errorf("After resize Cols = %d, want 120", sess.Cols)
	}
	if sess.Rows != 40 {
		t.Errorf("After resize Rows = %d, want 40", sess.Rows)
	}
}

func TestResizeNonexistent(t *testing.T) {
	p := NewPool()
	err := p.Resize("nonexistent", 80, 24)
	if err == nil {
		t.Fatal("Resize on nonexistent session should return error")
	}
}

func TestCloseAll(t *testing.T) {
	name := "test-pool-closeall"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()

	_, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Attach failed: %v", err)
	}

	p.CloseAll()

	// Give the background goroutine time to clean up
	time.Sleep(100 * time.Millisecond)

	if p.IsAlive(name) {
		t.Fatal("IsAlive should return false after CloseAll")
	}
}

func TestAttachCleansUpDeadSession(t *testing.T) {
	name := "test-pool-dead-cleanup"
	cleanup := setupTmuxSession(t, name)
	defer cleanup()

	p := NewPool()
	defer p.CloseAll()

	sess1, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("First Attach failed: %v", err)
	}

	// Kill the process to simulate a dead session
	sess1.Cmd.Process.Kill()
	sess1.Cmd.Process.Wait()
	time.Sleep(100 * time.Millisecond)

	// Re-create the tmux session since killing the attach killed our handle
	exec.Command("tmux", "kill-session", "-t", name).Run()
	setupCmd := exec.Command("tmux", "new-session", "-d", "-s", name, "-x", "80", "-y", "24")
	if err := setupCmd.Run(); err != nil {
		t.Fatalf("failed to recreate tmux session: %v", err)
	}

	sess2, err := p.Attach(name, 80, 24)
	if err != nil {
		t.Fatalf("Second Attach after dead session failed: %v", err)
	}

	if sess1 == sess2 {
		t.Fatal("Attach should create new session when old one is dead")
	}
}
