package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/juju/errors"
	"github.com/pingcap/schrodinger-test/prepare"
	client "github.com/pingcap/schrodinger/pkg/test_client"
)

var (
	beforeWarmUp       time.Duration
	afterWarmUp        time.Duration
	backups            string
	sysbenchDBName     string
	sysbenchTableCount int
	sysbenchTableSize  int
)

func init() {
	flag.DurationVar(&beforeWarmUp, "before-warm-up", 3*time.Minute, "warm up time before start to backup cluster")
	flag.DurationVar(&afterWarmUp, "after-warm-up", 3*time.Minute, "warm up time after backup cluster")
	flag.StringVar(&backups, "backups", "empty,sysbench", "backup tidb cluster, default values: 'empty,sysbench'")
	flag.StringVar(&sysbenchDBName, "sysbench-db", "test", "the name of the database")
	flag.IntVar(&sysbenchTableCount, "sysbench-table-count", 16, "the number of tables")
	flag.IntVar(&sysbenchTableSize, "sysbench-table-size", 10000000, "number of rows per table")
}

var backupMap = map[string]backupFunc{
	"empty":    emptyBackup,
	"sysbench": sysbenchBackup,
}

type backupFunc func(cli *client.Client) error

func main() {
	client.Info("[backup] start...")
	flag.Parse()

	ctx, cancel := context.WithCancel(context.Background())
	cli := client.New(ctx, client.ModeOnline)

	sc := make(chan os.Signal, 1)
	signal.Notify(sc,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	go func() {
		sig := <-sc
		client.Infof("Got signal [%d] to exist", sig)
		cancel()
		os.Exit(0)
	}()

	backupsArr := formatString(backups)

	for _, backup := range backupsArr {
		f, exist := backupMap[backup]
		if !exist {
			client.Fatalf("[backup] %s not supported", backup)
		}
		client.Infof("[backup] waiting for %s before start to backup %s", beforeWarmUp.String(), backup)
		time.Sleep(beforeWarmUp)
		if err := f(cli); err != nil {
			client.Fatalf("[backup] return with error: %v", errors.ErrorStack(err))
		}
		client.Infof("[backup] waiting for %s after backup %s", afterWarmUp.String(), backup)
		time.Sleep(afterWarmUp)
	}

	client.Info("[backup] end...")
}

func emptyBackup(cli *client.Client) error {
	client.Info("[backup] start to backup empty cat")
	if err := cli.BackupCat("empty"); err != nil {
		client.Errorf("[backup] backup empty cat failed: %v", err)
		return errors.Trace(err)
	}

	return nil
}

func sysbenchBackup(cli *client.Client) error {
	backupName := fmt.Sprintf("sysbench-%d-%dw", sysbenchTableCount, sysbenchTableSize/10000)
	client.Infof("[backup] start to backup %s cat", backupName)

	client.Info("[backup] start to prepare sysbench data")
	if err := prepare.PrepareData(cli, sysbenchDBName, sysbenchTableCount, sysbenchTableSize); err != nil {
		client.Errorf("[backup] prepare sysbench data failed: %v", err)
		return errors.Trace(err)
	}
	client.Infof("[backup] sleep %s before to exec backup action", beforeWarmUp.String())
	time.Sleep(beforeWarmUp)

	client.Info("[backup] start to exec backup action")
	if err := cli.BackupCat(backupName); err != nil {
		client.Errorf("[backup] backup empty cat failed: %v", err)
		return errors.Trace(err)
	}

	return nil
}

func formatString(str string) []string {
	arr := strings.Split(str, ",")
	var results []string
	for _, s := range arr {
		s = strings.TrimSpace(s)
		if _, exist := backupMap[s]; exist {
			results = append(results, s)
		}
	}
	return results
}
