package random

import (
	"flag"
	"fmt"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"syscall"
	"testing"
	"time"

	"github.com/pingcap/log"
	"github.com/tidbcloud/ffs/tests"
	"github.com/tidbcloud/ffs/util/logutil"
)

var (
	randomTest = flag.Bool("random", false, "")
	randomStop = flag.Bool("random-stop", false, "")
	randomAddr = flag.String("random-addr", ":10100", "")
	LogLevel   = flag.String("log-level", "debug", "log level: info, debug, warn, error, fatal")
)

func init() {
	logutil.SetLevel(*LogLevel)
}

func TestRandom(t *testing.T) {
	if !*randomTest {
		log.S().Info("skip random test")
		return
	}
	cluster := tests.NewCluster(t)
	defer cluster.Stop()
	cluster.Start()
	log.S().Infof("client dirs %v", cluster.ClientDirs)
	RandomStop(cluster)
	mux := http.DefaultServeMux
	mux.HandleFunc("/client/start", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			for i := 0; i < len(cluster.StartClients); i++ {
				log.S().Infof("Start client %d", i)
				cluster.StartClient(i)
			}
			w.Write([]byte(fmt.Sprintf("start clients %d\n", len(cluster.StartClients))))
		} else {
			http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/client/stop", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			for i := 0; i < len(cluster.StopClients); i++ {
				stop := cluster.StopClients[i]
				if stop != nil {
					log.S().Infof("Stop client %d", i)
					stop()
				}
			}
			w.Write([]byte(fmt.Sprintf("stop clients %d\n", len(cluster.StopClients))))
		} else {
			http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/realm/start", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			for i := 0; i < len(cluster.StartRealms); i++ {
				log.S().Infof("Start realm %d", i)
				cluster.StartRealm(i)
			}
			w.Write([]byte(fmt.Sprintf("start realms %d\n", len(cluster.StartRealms))))
		} else {
			http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/realm/stop", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			for i := 0; i < len(cluster.StopRealms); i++ {
				stop := cluster.StopRealms[i]
				if stop != nil {
					log.S().Infof("Stop realm %d", i)
					stop()
				}
			}
			w.Write([]byte(fmt.Sprintf("stop realms %d\n", len(cluster.StopRealms))))
		} else {
			http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		}
	})
	go http.ListenAndServe(*randomAddr, mux)
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	log.S().Infof("use ctrl+c to terminate.")
	sig := <-sigCh
	log.S().Infof("got signal [%s] to exit.", sig)
}

func RandomStop(cluster *tests.Cluster) {
	if !*randomStop {
		return
	}
	go func() {
		RandomStopUniverse(cluster)
	}()
	go func() {
		RandomStopZone(cluster)
	}()
}

func RandomStopUniverse(cluster *tests.Cluster) {
	for {
		time.Sleep(time.Second * 30)
		indexes := tests.GetRandomIndexes(len(cluster.Universes))
		stopIndex := indexes[0]
		cluster.StopUniverses[stopIndex]()
		timer := time.NewTimer(time.Second * time.Duration(rand.Intn(300)))
		select {
		case <-timer.C:
		case <-cluster.Done():
			timer.Stop()
			return
		}
		cluster.StartUniverse(stopIndex)
		time.Sleep(time.Second * 10)
	}
}

func RandomStopZone(cluster *tests.Cluster) {
	for {
		time.Sleep(time.Second * 30)
		indexes := tests.GetRandomIndexes(len(cluster.Zones))
		stopIndex := indexes[0]
		cluster.StopZones[stopIndex]()
		timer := time.NewTimer(time.Second * time.Duration(rand.Intn(300)))
		select {
		case <-timer.C:
		case <-cluster.Done():
			timer.Stop()
			return
		}
		cluster.StartZone(stopIndex)
		time.Sleep(time.Second * 10)
	}
}
