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")
	tables      = flag.Int("tables", 1, "the number of the tables")
	concurrency = flag.Int("concurrency", 200, "concurrency worker count")
	retryLimit  = flag.Int("retry-limit", 200, "retry count")
	longTxn     = flag.Bool("long-txn", true, "enable long-term transactions")
	mode        = flag.String("mode", "online", "case mode, support values: online / dev, default value: online")
	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 (
	defaultVerifyTimeout = 6 * time.Hour
	remark               = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXVZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXVZlkjsanksqiszndqpijdslnnq"
)
var (
	TiDBDatabase = true
)

func main() {
	client.Info("[bank] start bank")
	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("[bank] Invalid mode: %v,support values: online / dev", mode)
		}
		client.Info("Mode:", clientMode)
		return clientMode
	}(*mode))

	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" {
		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("[bank] create db client error %v", err)
	}
	_, err = db.Exec("select tidb_version();")
	if err != nil {
		TiDBDatabase = false
		client.Info("[bank] select tidb_version(): %v", err)
	}

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

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

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

	time.Sleep(5 * time.Second)

	db, err = util.OpenDB(dbDSN, *concurrency)
	if err != nil {
		client.Fatalf("[bank] 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("[bank] Got signal [%s] to exist.", sig)
		cancel()
		os.Exit(0)
	}()

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

	cfg := Config{
		NumAccounts:   *accounts,
		Interval:      *interval,
		TableNum:      *tables,
		Concurrency:   *concurrency,
		EnableLongTxn: *longTxn,
	}
	bank := NewBankCase(&cfg)
	if err := bank.Initialize(ctx, db); err != nil {
		client.Fatalf("[bank] initial failed %v", err)
	}

	if err := bank.Execute(ctx, db); err != nil {
		client.Fatalf("[bank] return with error %v", err)
	}
}
