package test

import (
	"context"
	"testing"

	"github.com/dolphindb/api-go/api"
	. "github.com/smartystreets/goconvey/convey"

	"github.com/dolphindb/go-plugins/internal/telegraf/outputs"
)

const (
	DefaultTableName    = "tableTest"
	DefaultMemTableName = "t"
)

var (
	DefaultCfg = &outputs.Config{
		Address:      "192.168.1.99:31099",
		User:         "admin",
		Password:     "123456",
		Database:     "dfs://dbTest",
		TableName:    "tableTest",
		MetricName:   "disk",
		PartitionCol: "id",
	}
)

type TableColumn struct {
	Name string
	Type string
}

type PartitionTableConfig struct {
	DatabaseRequest         *api.DatabaseRequest
	PartitionedTableRequest *api.CreatePartitionedTableRequest
	MemTableRequest         *api.TableWithCapacityRequest
}

func DoTestInPartitionTable(testName string, t *testing.T,
	cfg *outputs.Config, req *PartitionTableConfig,
	fn func(cli api.DolphinDB, db *api.Database)) {
	var cli api.DolphinDB
	var db *api.Database
	Convey(testName, t, func() {
		cli = prepareClient(cfg)
		defer dropClient(cli)

		db = prepareDatabase(cli, req.DatabaseRequest)
		defer dropDatabase(cli, req.DatabaseRequest.Directory)

		prepareMemTable(cli, req.MemTableRequest)
		preparePartitionedTable(cli, req.DatabaseRequest.Directory, db, req.PartitionedTableRequest)

		fn(cli, db)
	})
}

func prepareClient(cfg *outputs.Config) api.DolphinDB {
	cli, err := api.NewSimpleDolphinDBClient(context.TODO(), cfg.Address, cfg.User, cfg.Password)
	So(err, ShouldBeNil)
	return cli
}

func dropClient(cli api.DolphinDB) {
	err := cli.Close()
	So(err, ShouldBeNil)
}

func prepareDatabase(cli api.DolphinDB, req *api.DatabaseRequest) *api.Database {
	re1, err := cli.ExistsDatabase(&api.ExistsDatabaseRequest{Path: req.Directory})
	So(err, ShouldBeNil)

	if re1 {
		dropDatabase(cli, req.Directory)
	}

	db, err := cli.Database(req)
	So(err, ShouldBeNil)
	return db
}

func loadOrNewDatabase(cli api.DolphinDB, req *api.DatabaseRequest) *api.Database {
	re1, err := cli.ExistsDatabase(&api.ExistsDatabaseRequest{Path: req.Directory})
	So(err, ShouldBeNil)

	if !re1 {
		db, err := cli.Database(req)
		So(err, ShouldBeNil)
		return db
	}

	db, err := cli.Database(&api.DatabaseRequest{
		DBHandle:  req.DBHandle,
		Directory: req.Directory,
	})
	So(err, ShouldBeNil)
	return db
}

func dropDatabase(cli api.DolphinDB, dir string) {
	err := cli.DropDatabase(&api.DropDatabaseRequest{Directory: dir})
	So(err, ShouldBeNil)
}

func generateMemTableRequestForPartition(cols []TableColumn) *api.TableWithCapacityRequest {
	req := &api.TableWithCapacityRequest{
		TableName: DefaultMemTableName,
		Capacity:  1,
		Size:      0,
		ColNames:  make([]string, len(cols)),
		ColTypes:  make([]string, len(cols)),
	}

	for k, v := range cols {
		req.ColNames[k] = v.Name
		req.ColTypes[k] = v.Type
	}

	return req
}

func prepareMemTable(cli api.DolphinDB, req *api.TableWithCapacityRequest) *api.Table {
	_, err := cli.RunScript("n=10")
	So(err, ShouldBeNil)

	t, err := cli.TableWithCapacity(req)
	So(err, ShouldBeNil)

	return t
}

func preparePartitionedTable(cli api.DolphinDB, dbPath string, db *api.Database, req *api.CreatePartitionedTableRequest) *api.Table {
	re1, err := cli.ExistsTable(&api.ExistsTableRequest{
		TableName: req.PartitionedTableName,
		DBPath:    dbPath,
	})
	So(err, ShouldBeNil)

	if re1 {
		dropTable(cli, dbPath, db.Name, req.PartitionedTableName)
	}

	t, err := db.CreatePartitionedTable(req)
	So(err, ShouldBeNil)
	return t
}

func CreatePartitionedTableIfNotExist(cli api.DolphinDB, dbPath string, db *api.Database, req *api.CreatePartitionedTableRequest) {
	re1, err := cli.ExistsTable(&api.ExistsTableRequest{
		TableName: req.PartitionedTableName,
		DBPath:    dbPath,
	})
	So(err, ShouldBeNil)

	if re1 {
		return
	}

	_, err = db.CreatePartitionedTable(req)
	So(err, ShouldBeNil)
}

func dropTable(cli api.DolphinDB, dbPath, dbHandle, tableName string) {
	err := cli.DropTable(&api.DropTableRequest{
		DBPath:    dbPath,
		DBHandle:  dbHandle,
		TableName: tableName,
	})
	So(err, ShouldBeNil)
}
