package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"

	"github.com/juju/errors"
	"github.com/pingcap/schrodinger/pkg/test_client"
	"golang.org/x/net/context"
)

var mode string

func init() {
	flag.StringVar(&mode, "mode", "normal", "panic mode. can be full or random")
}

func main() {
	client.Info("[upgrade] start...")

	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("[upgrade] Got signal [%s] to exist.", sig)
		cancel()
		os.Exit(0)
	}()

	var pairs []*Pair
	var err error
	if mode == "normal" {
		pairs, err = runNormalUpgrade(cli)
	} else {
		pairs, err = runFullUpgrade(cli)
	}
	if err != nil {
		client.Fatalf("run failed %+v", err)
	}
	client.Debugf("result:%+v", pairs)
	var line string
	var panic bool
	for _, p := range pairs {
		if p.Status != "SUCCESS" {
			panic = true
		}
		line += fmt.Sprintf("source: %s, target:%s statue:%s, error:%s", p.Source, p.Target, p.Status, p.Errors)
	}

	if panic {
		client.Fatalf(line)
	} else {
		client.Infof(line)
	}
}

func runNormalUpgrade(cli *client.Client) ([]*Pair, error) {
	var pairs []*Pair
	sourceVersions := getSourceCatVersions()
	targetVersion := getTargetCatVersion(cli)
	for _, version := range sourceVersions {
		p := &Pair{
			Source: version.Tidb.Value,
			Target: targetVersion.Tidb.Value,
		}
		client.Infof("start to upgrade cluster %v, target version %v", version, targetVersion)
		err := upgradeAndVerify(cli, version, targetVersion)
		if err != nil {
			p.Status = "FAIL"
			p.Errors = err.Error()
			pairs = append(pairs, p)
			client.Errorf("upgrade error%v", err)
			continue
		}

		p.Status = "SUCCESS"
		pairs = append(pairs, p)
		client.Infof("upgrade cluster success %v", version)
	}
	return pairs, nil
}

func runFullUpgrade(cli *client.Client) ([]*Pair, error) {
	var pairs []*Pair
	versions, err := getVersions()
	if err != nil {
		return nil, errors.Trace(err)
	}
	for _, version := range versions {
		vers := strings.Split(version, " ")
		client.Infof("start to upgrade cluster %v, target version %v", vers[0], vers[1])
		p := &Pair{
			Source: vers[0],
			Target: vers[1],
		}

		ver := buildVersion(vers[0])
		targetVersion := buildVersion(vers[1])

		err := upgradeAndVerify(cli, ver, targetVersion)
		if err != nil {
			p.Status = "FAIL"
			p.Errors = err.Error()
			pairs = append(pairs, p)
			client.Errorf("upgrade error%v", err)
			continue
		}

		p.Status = "SUCCESS"
		pairs = append(pairs, p)
		client.Infof("upgrade cluster success %v", version)
	}
	return pairs, nil

}

func upgradeAndVerify(cli *client.Client, version *Version, targetVersion *Version) error {
	ch := make(chan int, 10)
	// start cat
	err := replaceCat(cli, version)
	if err != nil {
		return errors.Trace(err)
	}

	go dateImport(cli, ch)

	<-ch

	go testDelete(cli, ch)
	go testMixed(cli, ch)
	wg := sync.WaitGroup{}

	execParallelDDL(cli, &wg, parallelDDLTbls...)
	err = upgradeCat(cli, targetVersion)
	if err != nil {
		return errors.Trace(err)
	}

	for i := 0; i < 2; i++ {
		<-ch
	}
	wg.Wait()
	checkParallelDDLTables(cli)
	return nil
}
