package core

import (
	"context"
	"testing"
	"time"
)

func TestSubprocessStream(t *testing.T) {
	t.Run("echo stdout llega como evento y ExitCode 0", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		events, results := SubprocessStream(ctx, "echo", []string{"hola"}, nil, nil)

		var got []StreamEvent
		for ev := range events {
			got = append(got, ev)
		}

		res := <-results

		if res.ExitCode != 0 {
			t.Errorf("ExitCode = %d, want 0 (err: %v)", res.ExitCode, res.Err)
		}
		if res.Err != nil {
			t.Errorf("unexpected Err: %v", res.Err)
		}
		if len(got) != 1 {
			t.Fatalf("got %d events, want 1", len(got))
		}
		if got[0].Stream != "stdout" {
			t.Errorf("Stream = %q, want %q", got[0].Stream, "stdout")
		}
		if got[0].Line != "hola" {
			t.Errorf("Line = %q, want %q", got[0].Line, "hola")
		}
	})

	t.Run("stderr llega como evento con stream stderr", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		// sh -c "echo msg >&2" escribe a stderr
		events, results := SubprocessStream(ctx, "sh", []string{"-c", "echo error_msg >&2"}, nil, nil)

		var got []StreamEvent
		for ev := range events {
			got = append(got, ev)
		}
		res := <-results

		if res.ExitCode != 0 {
			t.Errorf("ExitCode = %d, want 0", res.ExitCode)
		}
		if len(got) != 1 {
			t.Fatalf("got %d events, want 1", len(got))
		}
		if got[0].Stream != "stderr" {
			t.Errorf("Stream = %q, want %q", got[0].Stream, "stderr")
		}
		if got[0].Line != "error_msg" {
			t.Errorf("Line = %q, want %q", got[0].Line, "error_msg")
		}
	})

	t.Run("exit code no-cero se reporta en StreamResult", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		events, results := SubprocessStream(ctx, "sh", []string{"-c", "exit 42"}, nil, nil)

		for range events {
		}
		res := <-results

		if res.ExitCode != 42 {
			t.Errorf("ExitCode = %d, want 42", res.ExitCode)
		}
		if res.Err != nil {
			t.Errorf("unexpected Err: %v", res.Err)
		}
	})

	t.Run("ctx cancelado termina el proceso", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		// proceso que dura mucho; cancelamos enseguida
		ctxShort, cancelShort := context.WithTimeout(ctx, 100*time.Millisecond)
		defer cancelShort()

		events, results := SubprocessStream(ctxShort, "sleep", []string{"60"}, nil, nil)

		for range events {
		}
		res := <-results

		// Tras cancelacion el proceso debe haber terminado (ExitCode != 0 o Err de ctx)
		if res.ExitCode == 0 && res.Err == nil {
			t.Error("expected non-zero exit or ctx error after cancellation")
		}
		if res.DurationMs > 3000 {
			t.Errorf("took %d ms, expected < 3000 (should have been killed)", res.DurationMs)
		}
	})

	t.Run("multiples lineas stdout", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		events, results := SubprocessStream(ctx, "sh", []string{"-c", "printf 'a\nb\nc\n'"}, nil, nil)

		var lines []string
		for ev := range events {
			if ev.Stream == "stdout" {
				lines = append(lines, ev.Line)
			}
		}
		<-results

		if len(lines) != 3 {
			t.Fatalf("got %d stdout lines, want 3: %v", len(lines), lines)
		}
		want := []string{"a", "b", "c"}
		for i, w := range want {
			if lines[i] != w {
				t.Errorf("line[%d] = %q, want %q", i, lines[i], w)
			}
		}
	})
}
