package main

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/pingcap/schrodinger-test/util"
	client "github.com/pingcap/schrodinger/pkg/test_client"
	"github.com/pingcap/schrodinger/pkg/types"
)

type LargeTables struct {
	DbName          string
	DbCount         int
	TableCount      int
	TableBatchCount int
	MaxTime         int
	Transaction     bool
}

func (largeTables *LargeTables) run(cli *client.Client, db *sql.DB) (*types.TestResult, error) {
	tablesCount := (largeTables.TableCount + largeTables.TableBatchCount - 1) / largeTables.TableBatchCount * largeTables.TableBatchCount
	maxSingleTime := time.Duration(0)
	totalTime := time.Duration(0)
	warningTime := time.Millisecond * time.Duration(largeTables.MaxTime)

	for dbindex := 1; dbindex <= largeTables.DbCount; dbindex++ {
		maxTimeOneDb := time.Duration(0)
		dbUseTime := time.Duration(0)
		databasename := fmt.Sprintf("%s_%08d", largeTables.DbName, dbindex)
		strDropDatabase := fmt.Sprintf("DROP DATABASE IF EXISTS %s;", databasename)
		_, err := db.Exec(strDropDatabase)
		if err != nil {
			client.Fatalf("[bench_largetables] DROP DATABASE error %v,%s", err, strDropDatabase)
		}

		strCreateDatabase := fmt.Sprintf(`CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARSET utf8mb4;`, databasename)
		dbSartTime := time.Now()
		_, err = db.Exec(strCreateDatabase)
		if err != nil {
			client.Fatalf("[bench_largetables] CREATE DATABASE error %v", err)
		}
		dbUseTime = time.Now().Sub(dbSartTime)

		strCreateTable := `CREATE TABLE %s.T_%08d (
		k BIGINT UNSIGNED NOT NULL DEFAULT '0',
		c varchar(255) NOT NULL,
		pad varchar(60) NOT NULL DEFAULT '',
		PRIMARY KEY (k)
	  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
	  `

		for index := 1; index <= tablesCount; index += largeTables.TableBatchCount {
			strCreateBatch := ""
			if largeTables.Transaction {
				strCreateBatch += "begin;"
			}
			for i := index; i < index+largeTables.TableBatchCount; i++ {
				strCreateBatch += fmt.Sprintf(strCreateTable, databasename, i)
			}
			if largeTables.Transaction {
				strCreateBatch += "commit;"
			}

			startTime := time.Now()
			util.MustExec(db, strCreateBatch)
			useTime := time.Now().Sub(startTime)
			totalTime += useTime
			singleTime := useTime / time.Duration(largeTables.TableBatchCount)

			if singleTime > maxTimeOneDb {
				maxTimeOneDb = singleTime
			}
			if singleTime > warningTime {
				client.Fatalf("create table use:%v,larger than %v", singleTime, warningTime)
			}

			client.Infof("db:%d, Table:%d~%d, use:%.3fs, avg:%.4fs, max:%.4fs", dbindex, index, index+largeTables.TableBatchCount-1, useTime.Seconds(), singleTime.Seconds(), maxTimeOneDb.Seconds())
		}
		if maxTimeOneDb > maxSingleTime {
			maxSingleTime = maxTimeOneDb
		}
		client.Infof("dbTime:%.4fs, table avg:%.4fs, table max:%.4fs", dbUseTime.Seconds(), totalTime.Seconds()/float64(dbindex*tablesCount), maxSingleTime.Seconds())
	}

	return &types.TestResult{
		TestName: "bench_largetables",
		QPS:      float64(largeTables.DbCount*tablesCount) / totalTime.Seconds(),
		AvgMs:    totalTime.Seconds() * 1000. / float64(tablesCount*largeTables.DbCount),
		Pct99:    maxSingleTime.Seconds() * 1000.,
	}, nil
}
