package main

/*
   We start up a rando response server that will give different response codes
   and different response times to simulate poor network / service conditions.

   The main function is cut into blocks to perserve variable scope and examples
   of each pester function can be seen in action.

   The server logs incoming requests while the main blocks log what they intend
   to do and what they get back.
*/

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/sethgrid/pester"
)

func init() {
	rand.Seed(time.Now().Unix())
}

func main() {
	// set everything up
	var port int
	flag.IntVar(&port, "port", 9000, "set the port for the rando response server")
	flag.Parse()

	log.Printf("Starting a rando response server on :%d ...\n\n", port)

	go func() {
		http.HandleFunc("/", randoHandler)
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
	}()

	//////////////////////////////////////////////////////
	// begin running through each of the pestor methods //
	//////////////////////////////////////////////////////

	log.Println("> pester.Get default")
	{ // drop in replacement for http.Get and other client methods
		resp, err := pester.Get(fmt.Sprintf("http://localhost:%d", port))
		if err != nil {
			log.Fatalf("error GETing default", err)
		}
		defer resp.Body.Close()

		log.Printf("GET :%d %s \n\n", port, resp.Status)
	}

	log.Println("> pester.Get with set backoff stategy, concurrency and retries increased")
	{ // control the resiliency
		client := pester.New()
		client.Concurrency = 3
		client.MaxRetries = 5
		client.Backoff = pester.ExponentialJitterBackoff
		client.KeepLog = true

		resp, err := client.Get(fmt.Sprintf("http://localhost:%d", port))
		if err != nil {
			log.Fatalf("error GETing with all options, %s\n\n", client.LogString())
		}
		defer resp.Body.Close()

		log.Printf("Exponential Jitter Backoff :%d %s [request %d, retry %d]\n\n", port, resp.Status, client.SuccessReqNum, client.SuccessRetryNum)
	}

	log.Println("> pester.Get with custom backoff strategy")
	{ // set a custom backoff strategy
		client := pester.New()
		client.Backoff = func(retry int) time.Duration {
			return time.Duration(retry*200) * time.Millisecond
		}
		client.Timeout = 5 * time.Second
		client.KeepLog = true

		resp, err := client.Get(fmt.Sprintf("http://localhost:%d", port))
		if err != nil {
			log.Fatalf("error GETing custom backoff\n\n", client.LogString())
		}
		defer resp.Body.Close()

		log.Printf("Custom backoff :%d %s [request %d, retry %d]\n\n", port, resp.Status, client.SuccessReqNum, client.SuccessRetryNum)
	}

	log.Println("> pester.Post with defaults")
	{ // use the pester.Post drop in replacement
		resp, err := pester.Post(fmt.Sprintf("http://localhost:%d", port), "text/plain", strings.NewReader("data"))
		if err != nil {
			log.Fatalf("error POSTing with defaults - %v\n\n", err)
		}
		defer resp.Body.Close()

		log.Printf("POST :%d %s\n\n", port, resp.Status)
	}

	log.Println("> pester.Head with defaults")
	{ // use the pester.Head drop in replacement
		resp, err := pester.Head(fmt.Sprintf("http://localhost:%d", port))
		if err != nil {
			log.Fatalf("error HEADing with defaults - %v\n\n", err)
		}
		defer resp.Body.Close()

		log.Printf("HEAD :%d %s\n\n", port, resp.Status)
	}

	log.Println("> pester.PostForm with defaults")
	{ // use the pester.Head drop in replacement
		resp, err := pester.PostForm(fmt.Sprintf("http://localhost:%d", port), url.Values{"param1": []string{"val1a", "val1b"}, "param2": []string{"val2"}})
		if err != nil {
			log.Fatalf("error POSTing a form with defaults - %v\n\n", err)
		}
		defer resp.Body.Close()

		log.Printf("POST (form) :%d %s\n\n", port, resp.Status)
	}

	log.Println("> pester Do with POST")
	{ // use the pester version of http.Client.Do
		req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d", port), strings.NewReader("data"))
		if err != nil {
			log.Fatal("Unable to create a new http request", err)
		}
		resp, err := pester.Do(req)
		if err != nil {
			log.Fatalf("error POSTing with Do() - %v\n\n", err)
		}
		defer resp.Body.Close()

		log.Printf("Do() POST :%d %s\n\n", port, resp.Status)
	}

}

// randoHandler will cause random delays and give random status responses
func randoHandler(w http.ResponseWriter, r *http.Request) {
	delay := rand.Intn(5000)
	var code int
	switch rand.Intn(10) {
	case 0:
		code = 404
	case 1:
		code = 400
	case 2:
		code = 501
	case 3:
		code = 500
	default:
		code = 200
	}

	log.Printf("incoming request on :9000 - will return %d in %d ms", code, delay)

	<-time.Tick(time.Duration(delay) * time.Millisecond)

	w.WriteHeader(code)
	w.Write([]byte(http.StatusText(code)))
}
