package chitin

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"golang.org/x/net/context"
)

func BenchmarkContext(b *testing.B) {
	// - make a context with a timeout
	// - add some values to the context
	// - submit a job to the work queue
	// - query some values from the context
	// - wait for either the response or the context cancelation signal

	type response struct {
		body []byte
		err  error
	}

	type request struct {
		ctx  context.Context
		resp chan response
	}

	type key int
	keyA, keyB, keyC := new(key), new(key), new(key)

	do := func(req request) {
		ctx := req.ctx

		body := []byte(`{"status":"OK"}`)
		var err error

		if have, want := ctx.Value(keyA).(string), "A"; have != want {
			err = fmt.Errorf("Value(keyA); %q != %q", have, want)
		}

		if have, want := ctx.Value(keyB).(string), "B"; have != want {
			err = fmt.Errorf("Value(keyB); %q != %q", have, want)
		}

		if have, want := ctx.Value(keyC).(string), "C"; have != want {
			err = fmt.Errorf("Value(keyC); %q != %q", have, want)
		}

		time.Sleep(1 * time.Microsecond)

		resp := response{
			body: body,
			err:  err,
		}

		select {
		case req.resp <- resp:
		case <-ctx.Done():
		}
	}

	var wg sync.WaitGroup
	defer wg.Wait()
	work := make(chan request, 1<<10)
	defer close(work)

	for i := 0; i < 1<<9; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for job := range work {
				do(job)
				select {
				case resp := <-job.resp:
					if resp.err != nil {
						b.Errorf("response error: %v", resp.err)
						continue
					}
					if have, want := string(resp.body), `{"status":"OK"}`; have != want {
						b.Errorf("body; %q != %q", have, want)
					}
				case <-job.ctx.Done():
				}
			}
		}()
	}

	for i := 0; i < b.N; i++ {
		ctx := context.Background()
		ctx, _ = context.WithTimeout(ctx, 1*time.Second)
		ctx = context.WithValue(ctx, keyA, "A")
		ctx = context.WithValue(ctx, keyB, "B")
		ctx = context.WithValue(ctx, keyC, "C")
		req := request{
			ctx:  ctx,
			resp: make(chan response, 1),
		}
		work <- req
	}
}
