package main

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

	"github.com/pingcap/schrodinger-test/util"
	client "github.com/pingcap/schrodinger/pkg/test_client"
	"golang.org/x/net/context"
)

var defaultPushMetricsInterval = 15 * time.Second

var (
	dbName      = flag.String("db", "test", "database name")
	pw          = flag.String("pw", "", "database password")
	accounts    = flag.Int("accounts", 1000000, "the number of accounts")
	interval    = flag.Duration("interval", 2*time.Second, "the interval")
	concurrency = flag.Int("concurrency", 200, "concurrency")
	retryLimit  = flag.Int("retry-limit", 200, "retry count")
	minLength   = flag.Int("min-value-length", 0, "minimum value inserted into rocksdb")
	maxLength   = flag.Int("max-value-length", 128, "maximum value inserted into rocksdb")
	runMode     = flag.String("run-mode", "online", "case mode, support values: online / dev, default value: online")
	contention  = flag.String("contention", "low", "contention level, support values: high / low, default value: low")
	pessimistic = flag.Bool("pessimistic", false, "use pessimistic transaction")
	replicaRead = flag.String("tidb-replica-read", "leader", "tidb_replica_read mode, support values: leader / follower / leader-and-follower, default value: leader.")
)
var (
	TiDBDatabase = true
)

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

	ctx, cancel := context.WithCancel(context.Background())
	cli := client.New(ctx, func(mode string) (clientMode client.Mode) {
		switch client.Mode(mode) {
		case client.ModeOnline:
			clientMode = client.ModeOnline
		case client.ModeDev:
			clientMode = client.ModeDev
		default:
			client.Fatalf("[bank2] Invalid mode: %v,support values: online / dev", mode)
		}
		client.Info("Mode:", clientMode)
		return clientMode
	}(*runMode))

	dbAddr := cli.TiDBService()
	var dbDSN string
	if *replicaRead == "leader" {
		dbDSN = fmt.Sprintf("root:%s@tcp(%s)/%s", *pw, dbAddr, *dbName)
	} else if *replicaRead == "follower" || *replicaRead == "leader-and-follower" {
		util.TestTiDBReplicaRead("bank2", fmt.Sprintf("root:%s@tcp(%s)/%s", *pw, dbAddr, *dbName))
		dbDSN = fmt.Sprintf("root:%s@tcp(%s)/%s?tidb_replica_read='%s'", *pw, dbAddr, *dbName, *replicaRead)
	} else {
		client.Fatalf("[bank2] Unsupported tidb_replica_read: %s", *replicaRead)
	}

	client.Info("dbDSN:", dbDSN)

	db, err := util.OpenDB(dbDSN, 1)
	if err != nil {
		client.Fatalf("[bank2] create db client error %v", err)
	}

	_, err = db.Exec("select tidb_version();")
	if err != nil {
		TiDBDatabase = false
		client.Info("[bank2] select tidb_version(): %v", err)
	}

	if TiDBDatabase {
		if *pessimistic {
			_, err = db.Exec("set @@global.tidb_txn_mode = 'pessimistic';")
			if err != nil {
				client.Fatalf("[bank2] set pessimistic failed: %v", err)
			}
		}

		var txnMode string
		if err = db.QueryRow("select @@tidb_txn_mode").Scan(&txnMode); err == nil {
			client.Infof("[bank2] Current txn mode: %v", txnMode)
		}
	}

	if err = db.Close(); err != nil {
		client.Fatalf("[bank2] fail to close set txn mode conn", err)
	}

	time.Sleep(5 * time.Second)

	db, err = util.OpenDB(dbDSN, *concurrency)
	if err != nil {
		client.Fatalf("[bank2] create db client error %v", err)
	}

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

	metricsAddr := cli.PrometheusAddr()
	if len(metricsAddr) > 0 {
		go util.PushPrometheus("bank2", metricsAddr, defaultPushMetricsInterval)
	}

	cfg := &Bank2CaseConfig{
		NumAccounts: *accounts,
		Interval:    *interval,
		Contention:  *contention,
		Concurrency: *concurrency,
		MinLength:   *minLength,
		MaxLength:   *maxLength,
	}
	client.Infof("config: %+v", cfg)

	bank := NewBank2Case(cfg)
	err = bank.Initialize(ctx, db)
	if err != nil {
		client.Fatalf("initialize %s error %v", bank, err)
	}

	err = bank.Execute(ctx, db)
	if err != nil {
		client.Fatalf("bank2 execution error %v", err)
	}
}
