package terminal_test

import (
	"context"
	"os"
	"strings"
	"testing"
	"time"

	"iop/apps/node/internal/terminal"
)

func TestTerminalSessionWritesPrompt(t *testing.T) {
	opts := terminal.Options{
		Command: "sh",
	}
	core, err := terminal.StartSession(context.Background(), opts)
	if err != nil {
		t.Fatalf("failed to start terminal session: %v", err)
	}
	defer core.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// Write prompt "echo hello-world" (WritePrompt will append \r)
	if err := core.WritePrompt(ctx, "echo hello-world"); err != nil {
		t.Fatalf("failed to write prompt: %v", err)
	}

	// Wait for the output to propagate
	var tail string
	success := false
	for {
		select {
		case <-ctx.Done():
			t.Fatalf("timeout waiting for output. tail was: %q", tail)
		case <-time.After(100 * time.Millisecond):
			tail = core.Snapshot().Tail
			if strings.Contains(tail, "hello-world") {
				success = true
				break
			}
		}
		if success {
			break
		}
	}
}

func TestTerminalSessionSnapshot(t *testing.T) {
	opts := terminal.Options{
		Command: "sh",
	}
	core, err := terminal.StartSession(context.Background(), opts)
	if err != nil {
		t.Fatalf("failed to start terminal session: %v", err)
	}
	defer core.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	if err := core.WritePrompt(ctx, "echo test-snapshot"); err != nil {
		t.Fatalf("failed to write prompt: %v", err)
	}

	time.Sleep(200 * time.Millisecond)
	snap := core.Snapshot()
	if !strings.Contains(snap.Tail, "test-snapshot") {
		t.Errorf("snapshot tail does not contain test-snapshot: %q", snap.Tail)
	}
}

func TestTerminalSessionCloseIsIdempotent(t *testing.T) {
	opts := terminal.Options{
		Command: "sh",
	}
	core, err := terminal.StartSession(context.Background(), opts)
	if err != nil {
		t.Fatalf("failed to start terminal session: %v", err)
	}

	if err := core.Close(); err != nil {
		t.Errorf("first close failed: %v", err)
	}

	if err := core.Close(); err != nil {
		t.Errorf("second close failed: %v", err)
	}
}

func TestTerminalSessionResizeValidatesBounds(t *testing.T) {
	opts := terminal.Options{
		Command: "sh",
	}
	core, err := terminal.StartSession(context.Background(), opts)
	if err != nil {
		t.Fatalf("failed to start terminal session: %v", err)
	}
	defer core.Close()

	if err := core.Resize(0, 80); err == nil {
		t.Error("expected error when resizing with rows=0")
	}

	if err := core.Resize(80, 0); err == nil {
		t.Error("expected error when resizing with cols=0")
	}

	if err := core.Resize(40, 120); err != nil {
		t.Errorf("failed to resize with valid dimensions: %v", err)
	}
}

func TestTerminalSessionRejectsWriteAfterClose(t *testing.T) {
	opts := terminal.Options{
		Command: "sh",
	}
	core, err := terminal.StartSession(context.Background(), opts)
	if err != nil {
		t.Fatalf("failed to start terminal session: %v", err)
	}

	if err := core.Close(); err != nil {
		t.Fatalf("close failed: %v", err)
	}

	ctx := context.Background()
	if err := core.WritePrompt(ctx, "ls"); err == nil {
		t.Error("expected WritePrompt to reject writing after close")
	}

	if err := core.WriteInput(ctx, []byte("ls\n")); err == nil {
		t.Error("expected WriteInput to reject writing after close")
	}
}

func TestTerminalSessionSignalAfterClose(t *testing.T) {
	opts := terminal.Options{
		Command: "sh",
	}
	core, err := terminal.StartSession(context.Background(), opts)
	if err != nil {
		t.Fatalf("failed to start terminal session: %v", err)
	}

	if err := core.Close(); err != nil {
		t.Fatalf("close failed: %v", err)
	}

	if err := core.Signal(os.Interrupt); err == nil {
		t.Error("expected Signal to reject signal after close")
	}
}
