package main

import (
	"errors"
	"fmt"
	"time"
)

type workItem struct {
	id   int
	data string
}

type workResult struct {
	id     int
	result string
}

type result struct {
	res workResult
	err error
}

// section: process
func processWorkItems(ws []workItem) ([]workResult, error) {
	ch := make(chan result) // UNBUFFERED - problem!

	for _, w := range ws {
		go func(w workItem) {
			res, err := processWorkItem(w)
			ch <- result{res, err} // BLOCKS if early return happens
		}(w)
	}

	var results []workResult
	for i := 0; i < len(ws); i++ {
		r := <-ch
		if r.err != nil {
			return nil, r.err // EARLY RETURN - remaining workers leak!
		}
		results = append(results, r.res)
	}
	return results, nil
}

// section: process

func processWorkItem(w workItem) (workResult, error) {
	time.Sleep(100 * time.Millisecond)
	if w.id == 3 {
		return workResult{}, errors.New("item 3 failed")
	}
	return workResult{id: w.id, result: fmt.Sprintf("processed %s", w.data)}, nil
}

func main() {
	items := []workItem{
		{id: 1, data: "a"},
		{id: 2, data: "b"},
		{id: 3, data: "c"}, // Will fail
		{id: 4, data: "d"},
		{id: 5, data: "e"},
	}

	results, err := processWorkItems(items)
	if err != nil {
		fmt.Println("Error:", err)
		// Workers 4 and 5 are now leaked!
	}
	fmt.Println("Results:", results)

	// Keep alive to observe leaked goroutines
	time.Sleep(500 * time.Millisecond)
	fmt.Println("Program ending - leaked goroutines are still blocked")
}
