package main

import (
	"context"
	"iter"
	"slices"
	"strings"
	"testing"
)

func TestFilterStopsUpstream(t *testing.T) {
	visited, closed := 0, false
	src := iter.Seq[int](func(yield func(int) bool) {
		defer func() { closed = true }()
		for i := 0; i < 10; i++ {
			visited++
			if !yield(i) {
				return
			}
		}
	})
	got := []int{}
	for n := range Filter(src, func(n int) bool { return n%2 == 0 }) {
		got = append(got, n)
		if n == 2 {
			break
		}
	}
	if !slices.Equal(got, []int{0, 2}) || visited != 3 || !closed {
		t.Fatalf("got=%v visited=%d closed=%v", got, visited, closed)
	}
}

func TestPullCleanup(t *testing.T) {
	closed := false
	seq := iter.Seq[int](func(yield func(int) bool) {
		defer func() { closed = true }()
		yield(42)
	})
	next, stop := iter.Pull(seq)
	defer stop()
	if v, ok := next(); v != 42 || !ok {
		t.Fatal(v, ok)
	}
	if closed {
		t.Fatal("iterator already returned")
	}
	stop()
	stop()
	if !closed {
		t.Fatal("missing cleanup")
	}
	if v, ok := next(); v != 0 || ok {
		t.Fatal(v, ok)
	}
}

func TestLinesError(t *testing.T) {
	count := 0
	for _, err := range Lines(strings.NewReader(strings.Repeat("x", 70_000))) {
		count++
		if err == nil {
			t.Fatal("expected scanner error")
		}
	}
	if count != 1 {
		t.Fatal(count)
	}
}

func TestCancel(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	if got := slices.Collect(UntilCanceled(ctx, 10)); len(got) != 0 {
		t.Fatal(got)
	}
}
