;; Go end-to-end tests — complete programs exercising lex+parse+
;; types+eval+sched+stdlib together. Each test runs a multi-line Go
;; program and inspects the final env.

(define go-e2e-test-count 0)
(define go-e2e-test-pass 0)
(define go-e2e-test-fails (list))

(define
  go-e2e-test
  (fn (name actual expected)
    (set! go-e2e-test-count (+ go-e2e-test-count 1))
    (if (= actual expected)
      (set! go-e2e-test-pass (+ go-e2e-test-pass 1))
      (append! go-e2e-test-fails
        {:name name :expected expected :actual actual}))))

(define
  go-e2e-env
  (go-env-extend
    (go-env-extend go-env-builtins "strings" go-std-strings)
    "strconv" go-std-strconv))

(define
  go-e2e-run
  (fn (src-list)
    (go-eval-program go-e2e-env (map go-parse src-list))))

;; ── 1. Sieve via boolean slice (no modulo needed) ────────────────
(go-e2e-test "e2e: sieve-of-Eratosthenes via boolean slice — count primes ≤ 30"
  (let ((env (go-e2e-run
              (list
                ;; sieve[i] true means i is COMPOSITE (saves the
                ;; default-bool initialisation for primes).
                "sieve := []bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}"
                "for p := 2; p < 31; p = p + 1 { if sieve[p] == false { for k := p + p; k < 31; k = k + p { sieve[k] = true } } }"
                "count := 0"
                "for i := 2; i < 31; i = i + 1 { if sieve[i] == false { count = count + 1 } }"))))
    (go-env-lookup env "count"))
  ;; primes ≤ 30: 2,3,5,7,11,13,17,19,23,29 = 10
  10)

;; ── 1b. Range-membership check (works without mod) ───────────────
(go-e2e-test "e2e: linear search across slice of strings"
  (let ((env (go-e2e-run
              (list
                "words := []string{\"apple\", \"banana\", \"cherry\", \"date\"}"
                "func indexOf(xs []string, target string) int { for i, v := range xs { if v == target { return i } } ; return -1 }"
                "i := indexOf(words, \"cherry\")"
                "missing := indexOf(words, \"xyz\")"))))
    (list (go-env-lookup env "i") (go-env-lookup env "missing")))
  (list 2 -1))

;; ── 2. Reverse a slice ───────────────────────────────────────────
(go-e2e-test "e2e: reverse a slice of ints"
  (let ((env (go-e2e-run
              (list
                "func reverse(xs []int) []int { r := []int{} ; for i := len(xs) - 1; i >= 0; i = i - 1 { r = append(r, xs[i]) } ; return r }"
                "out := reverse([]int{1, 2, 3, 4, 5})"))))
    (go-env-lookup env "out"))
  (list :go-slice (list 5 4 3 2 1)))

;; ── 3. Fibonacci (recursive) ─────────────────────────────────────
(go-e2e-test "e2e: fib(10) = 55"
  (let ((env (go-e2e-run
              (list
                "func fib(n int) int { if n < 2 { return n } ; return fib(n-1) + fib(n-2) }"
                "r := fib(10)"))))
    (go-env-lookup env "r"))
  55)

;; ── 4. Sum-of-squares via Map+Reduce ─────────────────────────────
(go-e2e-test "e2e: sum-of-squares 1..5 via Map+Reduce"
  (let ((env (go-e2e-run
              (list
                "func Map[T any, U any](xs []T, f func(T) U) []U { r := []int{} ; for i, v := range xs { r = append(r, f(v)) } ; return r }"
                "func Reduce[T any, U any](xs []T, seed U, f func(U, T) U) U { acc := seed ; for i, v := range xs { acc = f(acc, v) } ; return acc }"
                "func sq(x int) int { return x * x }"
                "func add(a int, b int) int { return a + b }"
                "squares := Map([]int{1, 2, 3, 4, 5}, sq)"
                "total := Reduce(squares, 0, add)"))))
    (go-env-lookup env "total"))
  ;; 1 + 4 + 9 + 16 + 25 = 55
  55)

;; ── 5. Word frequency counter ────────────────────────────────────
(go-e2e-test "e2e: word-frequency over a sentence"
  (let ((env (go-e2e-run
              (list
                "text := \"the quick brown fox jumps over the lazy dog the\""
                "words := strings.Split(text, \" \")"
                "counts := map[string]int{}"
                "for i, w := range words { counts[w] = counts[w] + 1 }"
                "the_count := counts[\"the\"]"
                "fox_count := counts[\"fox\"]"
                "dog_count := counts[\"dog\"]"))))
    (list (go-env-lookup env "the_count")
          (go-env-lookup env "fox_count")
          (go-env-lookup env "dog_count")))
  (list 3 1 1))

;; ── 6. Pipeline via channels ─────────────────────────────────────
(go-e2e-test "e2e: pipeline — generate, square, sum"
  (let ((env (go-e2e-run
              (list
                "func gen(c chan int, n int) { for i := 1; i <= n; i = i + 1 { c <- i } ; close(c) }"
                "func sq(in chan int, out chan int) { for v := range in { out <- v * v } ; close(out) }"
                "src := make()"
                "sqs := make()"
                "go gen(src, 4)"
                "go sq(src, sqs)"
                "total := 0"
                "for v := range sqs { total = total + v }"))))
    (go-env-lookup env "total"))
  ;; 1+4+9+16 = 30
  30)

;; ── 7. Worker pool draining a job channel ────────────────────────
(go-e2e-test "e2e: worker pool — sum of doubled jobs"
  (let ((env (go-e2e-run
              (list
                "func worker(jobs chan int, results chan int) { for j := range jobs { results <- j * 2 } }"
                "jobs := make()"
                "results := make()"
                "jobs <- 10 ; jobs <- 20 ; jobs <- 30"
                "close(jobs)"
                "go worker(jobs, results)"
                "close(results)"
                "sum := 0"
                "for r := range results { sum = sum + r }"))))
    (go-env-lookup env "sum"))
  ;; 20 + 40 + 60 = 120
  120)

;; ── 8. Bubble sort ───────────────────────────────────────────────
(go-e2e-test "e2e: bubble sort ascending"
  (let ((env (go-e2e-run
              (list
                "func bubble(xs []int) []int { n := len(xs) ; for i := 0; i < n; i = i + 1 { for j := 0; j < n - 1; j = j + 1 { if xs[j] > xs[j+1] { tmp := xs[j] ; xs[j] = xs[j+1] ; xs[j+1] = tmp } } } ; return xs }"
                "out := bubble([]int{3, 1, 4, 1, 5, 9, 2, 6})"))))
    (go-env-lookup env "out"))
  (list :go-slice (list 1 1 2 3 4 5 6 9)))

;; ── 9. String reverse using strings.Split + reverse + Join ──────
(go-e2e-test "e2e: reverse words in a sentence"
  (let ((env (go-e2e-run
              (list
                "func rev(xs []string) []string { r := []string{} ; for i := len(xs) - 1; i >= 0; i = i - 1 { r = append(r, xs[i]) } ; return r }"
                "text := \"go on sx\""
                "out := strings.Join(rev(strings.Split(text, \" \")), \"-\")"))))
    (go-env-lookup env "out"))
  "sx-on-go")

;; ── 10. Counting occurrences via Filter ──────────────────────────
(go-e2e-test "e2e: count even numbers via Filter+len"
  (let ((env (go-e2e-run
              (list
                "func Filter[T any](xs []T, p func(T) bool) []T { r := []int{} ; for i, v := range xs { if p(v) { r = append(r, v) } } ; return r }"
                "func gt5(x int) bool { return x > 5 }"
                "n := len(Filter([]int{1, 2, 6, 3, 7, 8, 4, 9}, gt5))"))))
    (go-env-lookup env "n"))
  ;; gt5: 6,7,8,9 = 4
  4)

;; ── 11. Recursive ackermann (small inputs) ───────────────────────
(go-e2e-test "e2e: ackermann(2, 3) = 9"
  (let ((env (go-e2e-run
              (list
                "func ack(m int, n int) int { if m == 0 { return n + 1 } ; if n == 0 { return ack(m - 1, 1) } ; return ack(m - 1, ack(m, n - 1)) }"
                "r := ack(2, 3)"))))
    (go-env-lookup env "r"))
  9)

;; ── 12. Defer + recover smoke test ───────────────────────────────
(go-e2e-test "e2e: defer + recover in real-fn flow"
  (let ((env (go-e2e-run
              (list
                "func safeDivide(a int, b int) int { defer recover() ; if b == 0 { panic(\"div by zero\") } ; return a / b }"
                "r := safeDivide(10, 0)"
                "after := 99"))))
    (go-env-lookup env "after"))
  99)

(define
  go-e2e-test-summary
  (str "e2e " go-e2e-test-pass "/" go-e2e-test-count))
