package main

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"sync"
)

var ErrFull = errors.New("queue full")
var ErrClosed = errors.New("pool closed")

type Job struct {
	Ctx    context.Context
	Run    func(context.Context) error
	Result chan error
}
type Pool struct {
	mu     sync.RWMutex
	closed bool
	jobs   chan Job
	done   chan struct{}
}

func NewPool(workers, queue int) *Pool {
	if workers < 1 || queue < 0 {
		panic("invalid pool size")
	}
	p := &Pool{jobs: make(chan Job, queue), done: make(chan struct{})}
	var wg sync.WaitGroup
	for range workers {
		wg.Go(func() {
			for j := range p.jobs {
				err := j.Ctx.Err()
				if err == nil {
					err = j.Run(j.Ctx)
				}
				j.Result <- err
				close(j.Result)
			}
		})
	}
	go func() { wg.Wait(); close(p.done) }()
	return p
}
func (p *Pool) TrySubmit(ctx context.Context, f func(context.Context) error) (<-chan error, error) {
	p.mu.RLock()
	defer p.mu.RUnlock()
	if p.closed {
		return nil, ErrClosed
	}
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	j := Job{ctx, f, make(chan error, 1)}
	select {
	case p.jobs <- j:
		return j.Result, nil
	default:
		return nil, ErrFull
	}
}
func (p *Pool) Shutdown(ctx context.Context) error {
	p.mu.Lock()
	if !p.closed {
		p.closed = true
		close(p.jobs)
	}
	p.mu.Unlock()
	select {
	case <-p.done:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Batch demonstrates count-based grouping and final flush, not a timed live batcher.
func Batch(xs []int, n int) [][]int {
	if n < 1 {
		panic("positive batch size required")
	}
	var out [][]int
	for len(xs) > 0 {
		k := min(n, len(xs))
		out = append(out, append([]int(nil), xs[:k]...))
		xs = xs[k:]
	}
	return out
}
func main() {
	ctx := context.Background()
	p := NewPool(1, 2)
	started := make(chan struct{})
	release := make(chan struct{})
	first, _ := p.TrySubmit(ctx, func(context.Context) error { close(started); <-release; return nil })
	<-started
	second, e2 := p.TrySubmit(ctx, func(context.Context) error { return nil })
	third, e3 := p.TrySubmit(ctx, func(context.Context) error { return nil })
	_, e4 := p.TrySubmit(ctx, func(context.Context) error { return nil })
	fmt.Println("queued:", e2 == nil, e3 == nil, "overflow rejected:", errors.Is(e4, ErrFull))
	close(release)
	fmt.Println("results:", <-first, <-second, <-third)
	fmt.Println("drained:", p.Shutdown(ctx) == nil)
	_, err := p.TrySubmit(ctx, func(context.Context) error { return nil })
	fmt.Println("submit after close:", errors.Is(err, ErrClosed))
	batches := Batch([]int{1, 2, 3, 4, 5}, 2)
	fmt.Println("batches:", batches)
	var reuse = sync.Pool{New: func() any { return new(bytes.Buffer) }}
	b := reuse.Get().(*bytes.Buffer)
	b.WriteString("tenant-a")
	b.Reset()
	reuse.Put(b)
	b = reuse.Get().(*bytes.Buffer)
	fmt.Println("reused buffer length:", b.Len())
	b.Reset()
	reuse.Put(b)
}
