package main

import (
	"log"
	"os"
	"strings"

	"github.com/go-yaml/yaml"
)

var testcfg = `# 暂时不支持ssl.
log_level: info
pg:
  host: 172.16.5.212
  port: 54322
  user: postgres
  pwd: 123456
tidb:
  host: 172.16.4.175
  port: 4000
  user: root
  pwd: 123456

do: checkdiff # prepare/checkdiff/checkrange
# 校验pg/tidb 之间数据校验.[默认值]
check_diff:
  nthread: 8 #一般跟pg的核心数相同，这样基本能吃满CPU（太大也没有意义）.
  limit: 30000
  dbs: [postgres]
# 往db中灌数据.
prepare:
  nthread: 64 # 可以是核心数*8，插入操作，cpu要求不高.
  to: pg # tidb
  total: 10100000
  db: postgres
  loop_size: 1000
  table: sbtest2
# 指定范围，对部分数据进行校验（在一次全量校验之后，发现某些数据校验失败，这里就需要在细粉、校验.)
check_range:
  nthread: 8
  db: postgres
  tbl: sbtest0
  count: 30000 # range 大小.
  range:
    -
      beg: 0
      end: 29999
    -
      beg: 453500
      end: 483749
`

type dbCfg struct {
	Host string `yaml:"host"`
	Port string `yaml:"port"`
	User string `yaml:"user"`
	Pwd  string `yaml:"pwd"`
}

type prepare struct {
	NThread int    `yaml:"nthread"`
	To      string `yaml:"to"` //pg/tidb
	Total   int    `yaml:"total"`
	OneTime int    `yaml:"loop_size"`
	Db      string `yaml:"db"`
	Table   string `yaml:"table"'`
}

type checkdiff struct {
	NThread int      `yaml:"nthread"`
	Limit   int      `yaml:"limit"`
	DBs     []string `yaml:"dbs"`
}

type checkRange struct {
	NThread int      `yaml:"nthread"`
	DB      string   `yaml:"db"`
	Tbl     string   `yaml:"tbl"`
	Count   int      `yaml:"count"`
	Ranges  []crange `yaml:"range"`
}

type crange struct {
	Start string `yaml:"beg"`
	End   string `yaml:"end"`
}

type Cfg struct {
	LogLevel string `yaml:"log_level"`
	PG   dbCfg      `yaml:"pg"`
	Tidb dbCfg      `yaml:"tidb"`
	Do   string     `yaml:"do"`
	CD   checkdiff  `yaml:"check_diff"`
	Pp   prepare    `yaml:"prepare"`
	CR   checkRange `yaml:"check_range"`
}

// prepare/checkdiff/checkrange
const (
	do_check_diff  = "checkdiff"
	do_prepare     = "prepare"
	do_check_range = "check_range"
)

func parseConfig(ccfg *Cfg) {
	fp, err := os.Open(*c)
	if err != nil {
		log.Fatalf("open config error:%s.you can use -h for help.\n", err.Error())
	}
	defer fp.Close()
	d := yaml.NewDecoder(fp)
	err = d.Decode(ccfg)
	if err != nil {
		log.Fatalf("parse config error:%s.you can use -h for example.\n", err.Error())
	}
	//
	switch ccfg.Do {
	case do_check_diff:
		checkCfgDBS(ccfg.CD.DBs)
		testDB(ccfg, ccfg.CD.DBs[0])
	case do_check_range:
		db := ccfg.CR.DB
		ret := checkCfgDBS([]string{db})
		db = ret[0]
		testDB(ccfg, db)
		ccfg.CR.DB = db
	case do_prepare:
		db := ccfg.Pp.Db
		ret := checkCfgDBS([]string{db})
		db = ret[0]
		testDB(ccfg, db)
		ccfg.Pp.Db = db
	}
}

func testDB(ccfg *Cfg, db string) {
	_, err := makePGDatabase(ccfg.PG, db)
	if err != nil {
		log.Fatalf("makePGDatabase err:%s", err.Error())
	}
	_, err = makeTidbDatabase(ccfg.Tidb, db)
	if err != nil {
		log.Fatalf("makeTidbDatabase err:%s", err.Error())
	}
}

func checkCfgDBS(dbs []string) []string {
	var ret []string
	for i := 0; i < len(dbs); i++ {
		v := strings.TrimSpace(dbs[i])
		if len(v) > 0 {
			ret = append(ret, v)
		}
	}
	if len(ret) == 0 {
		log.Fatalf("dbs mast not empty.you can use -h for help.\n")
	}
	return ret
}
