package goreplay

import (
	"bytes"
	"context"
	"os"
	"os/exec"
	"strings"
	"sync/atomic"
	"syscall"
	"testing"
)

const echoSh = "./examples/middleware/echo.sh"
const tokenModifier = "go run ./examples/middleware/token_modifier.go"

var withDebug = append(syscall.Environ(), "GOR_TEST=1")

func initMiddleware(cmd *exec.Cmd, cancl context.CancelFunc, l PluginReader, c func(error)) *Middleware {
	var m Middleware
	m.data = make(chan *Message, 1000)
	m.stop = make(chan bool)
	m.commandCancel = cancl
	m.Stdout, _ = cmd.StdoutPipe()
	m.Stdin, _ = cmd.StdinPipe()
	cmd.Stderr = os.Stderr
	go m.read(m.Stdout)
	go func() {
		defer m.Close()
		var err error
		if err = cmd.Start(); err == nil {
			err = cmd.Wait()
		}
		if err != nil {
			c(err)
		}
	}()
	m.ReadFrom(l)
	return &m
}

func initCmd(command string, env []string) (*exec.Cmd, context.CancelFunc) {
	commands := strings.Split(command, " ")
	ctx, cancl := context.WithCancel(context.Background())
	cmd := exec.CommandContext(ctx, commands[0], commands[1:]...)
	cmd.Env = env
	return cmd, cancl
}

func TestMiddlewareEarlyClose(t *testing.T) {
	quit := make(chan struct{})
	in := NewTestInput()
	cmd, cancl := initCmd(echoSh, withDebug)
	midd := initMiddleware(cmd, cancl, in, func(err error) {
		if err != nil {
			if e, ok := err.(*exec.ExitError); ok {
				status := e.Sys().(syscall.WaitStatus)
				if status.Signal() != syscall.SIGKILL {
					t.Errorf("expected error to be signal killed. got %s", status.Signal().String())
				}
			}
		}
		quit <- struct{}{}
	})
	var body = []byte("OPTIONS / HTTP/1.1\r\nHost: example.org\r\n\r\n")
	count := uint32(0)
	out := NewTestOutput(func(msg *Message) {
		if !bytes.Equal(body, msg.Data) {
			t.Errorf("expected %q to equal %q", body, msg.Data)
		}
		atomic.AddUint32(&count, 1)
		if atomic.LoadUint32(&count) == 5 {
			quit <- struct{}{}
		}
	})
	pl := &InOutPlugins{}
	pl.Inputs = []PluginReader{midd, in}
	pl.Outputs = []PluginWriter{out}
	pl.All = []interface{}{midd, out, in}
	e := NewEmitter()
	go e.Start(pl, "")
	for i := 0; i < 5; i++ {
		in.EmitBytes(body)
	}
	<-quit
	midd.Close()
	<-quit
}

//func TestTokenMiddleware(t *testing.T) {
//	quit := make(chan struct{})
//	in := NewTestInput()
//	in.skipHeader = true
//	cmd, cancl := initCmd(tokenModifier, withDebug)
//	midd := initMiddleware(cmd, cancl, in, func(err error) {})
//	req := []byte("1 932079936fa4306fc308d67588178d17d823647c 1439818823587396305 200\nGET /token HTTP/1.1\r\nHost: example.org\r\n\r\n")
//	res := []byte("2 932079936fa4306fc308d67588178d17d823647c 1439818823587396305 200\nHTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n17d823647c")
//	rep := []byte("3 932079936fa4306fc308d67588178d17d823647c 1439818823587396305 200\nHTTP/1.1 200 OK\r\nContent-Length: 15\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n932079936fa4306")
//	count := uint32(0)
//	out := NewTestOutput(func(msg *Message) {
//		if msg.Meta[0] == '1' && !bytes.Equal(payloadID(msg.Meta), payloadID(req)) {
//			token, _, _ := proto.PathParam(msg.Data, []byte("token"))
//			if !bytes.Equal(token, proto.Body(rep)) {
//				t.Errorf("expected the token %s to be equal to the replayed response's token %s", token, proto.Body(rep))
//			}
//		}
//		atomic.AddUint32(&count, 1)
//		if atomic.LoadUint32(&count) == 2 {
//			quit <- struct{}{}
//		}
//	})
//	pl := &InOutPlugins{}
//	pl.Inputs = []PluginReader{midd, in}
//	pl.Outputs = []PluginWriter{out}
//	pl.All = []interface{}{midd, out, in}
//	e := NewEmitter()
//	go e.Start(pl, "")
//	in.EmitBytes(req) // emit original request
//	in.EmitBytes(res) // emit its response
//	in.EmitBytes(rep) // emit replayed response
//	// emit the request which should have modified token
//	token := []byte("1 8e091765ae902fef8a2b7d9dd96 14398188235873 100\nGET /?token=17d823647c HTTP/1.1\r\nHost: example.org\r\n\r\n")
//	in.EmitBytes(token)
//	<-quit
//	midd.Close()
//}

//func TestMiddlewareWithPrettify(t *testing.T) {
//	Settings.PrettifyHTTP = true
//	quit := make(chan struct{})
//	in := NewTestInput()
//	cmd, cancl := initCmd(echoSh, withDebug)
//	midd := initMiddleware(cmd, cancl, in, func(err error) {})
//	var b1 = []byte("POST / HTTP/1.1\r\nHost: example.org\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n")
//	var b2 = []byte("POST / HTTP/1.1\r\nHost: example.org\r\nContent-Length: 25\r\n\r\nWikipedia in\r\n\r\nchunks.")
//	out := NewTestOutput(func(msg *Message) {
//		if !bytes.Equal(proto.Body(b2), proto.Body(msg.Data)) {
//			t.Errorf("expected %q body to equal %q body", b2, msg.Data)
//		}
//		quit <- struct{}{}
//	})
//	pl := &InOutPlugins{}
//	pl.Inputs = []PluginReader{midd, in}
//	pl.Outputs = []PluginWriter{out}
//	pl.All = []interface{}{midd, out, in}
//	e := NewEmitter()
//	go e.Start(pl, "")
//	in.EmitBytes(b1)
//	<-quit
//	midd.Close()
//	Settings.PrettifyHTTP = false
//}
