package oscmd

import (
	"reflect"
	"strings"
	"testing"
)

func TestRunTrimsStdout(t *testing.T) {
	out, err := Run("echo", "hello")
	if err != nil {
		t.Fatal(err)
	}
	if out != "hello" {
		t.Errorf("got %q, want %q", out, "hello")
	}
}

func TestRunSurfacesStderr(t *testing.T) {
	_, err := Run("sh", "-c", "echo boom >&2; exit 1")
	if err == nil || !strings.Contains(err.Error(), "boom") {
		t.Fatalf("expected stderr 'boom' in error, got %v", err)
	}
}

func TestRunMissingBinary(t *testing.T) {
	if _, err := Run("definitely-not-a-real-binary-xyz"); err == nil {
		t.Fatal("expected error for missing binary")
	}
}

func TestRunLines(t *testing.T) {
	got, err := RunLines("printf", `a\nb\nc\n`)
	if err != nil {
		t.Fatal(err)
	}
	want := []string{"a", "b", "c"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

func TestRunLinesEmpty(t *testing.T) {
	got, err := RunLines("true")
	if err != nil {
		t.Fatal(err)
	}
	if len(got) != 0 {
		t.Errorf("empty output should yield no lines, got %v", got)
	}
}

func TestOK(t *testing.T) {
	if OK().Body.Status != "ok" {
		t.Error("OK() should report status ok")
	}
}

func TestRunStdin(t *testing.T) {
	SetMock("cat", func(args []string) MockCommand {
		return MockCommand{Stdout: "piped input data", ExitCode: 0}
	})
	defer ClearMocks()

	got, err := RunStdin("some input", "cat")
	if err != nil {
		t.Fatal(err)
	}
	if got != "piped input data" {
		t.Errorf("got %q, want %q", got, "piped input data")
	}
}

func TestRunStatus(t *testing.T) {
	SetMock("check-update", func(args []string) MockCommand {
		return MockCommand{Stdout: "updates available", ExitCode: 100}
	})
	defer ClearMocks()

	out, code, err := RunStatus("check-update")
	if err != nil {
		t.Fatal(err)
	}
	if out != "updates available" || code != 100 {
		t.Errorf("got out=%q code=%d, want out=%q code=100", out, code, "updates available")
	}
}

func TestRunStream(t *testing.T) {
	SetMock("stream-cmd", func(args []string) MockCommand {
		return MockCommand{Lines: []string{"line1", "line2"}, DelayMs: 1}
	})
	defer ClearMocks()

	ch, err := RunStream(t.Context(), "stream-cmd")
	if err != nil {
		t.Fatal(err)
	}

	var got []string
	for line := range ch {
		got = append(got, line)
	}

	want := []string{"line1", "line2"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

func TestRunStreamCombined(t *testing.T) {
	SetMock("combined-cmd", func(args []string) MockCommand {
		return MockCommand{Lines: []string{"output1", "output2"}, ExitCode: 0}
	})
	defer ClearMocks()

	lines, errc, err := RunStreamCombined(t.Context(), nil, "combined-cmd")
	if err != nil {
		t.Fatal(err)
	}

	var got []string
	for line := range lines {
		got = append(got, line)
	}

	if err := <-errc; err != nil {
		t.Errorf("stream failed with error: %v", err)
	}

	want := []string{"output1", "output2"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}
