package test

import (
	"fmt"
	"strings"
	"time"

	"github.com/dolphindb/api-go/api"
	"github.com/dolphindb/api-go/model"
	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/metric"
	. "github.com/smartystreets/goconvey/convey"

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

const (
	MetricName       = "disk"
	SecondMetricName = "system"

	MetricTableName       = MetricName
	SecondMetricTableName = SecondMetricName

	MetricDatabase = "dfs://telegraf"
)

var (
	MetricItemSet = map[string]FieldValue{
		outputs.TimeKey: {Value: time.Now(), ValueType: model.DtDate},
		"host":          {Value: "localhost", ValueType: model.DtString},
		"total":         {Value: int64(9999), ValueType: model.DtLong},
		"used":          {Value: int64(99), ValueType: model.DtLong},
		"free":          {Value: int64(9900), ValueType: model.DtLong},
		"used_percent":  {Value: 24.105549203979386, ValueType: model.DtDouble},

		"uptime_format": {Value: "localhost", ValueType: model.DtString},
	}

	MetricColumnNameList       = []string{outputs.TimeKey, "host", "total", "used", "free", "used_percent"}
	SecondMetricColumnNameList = []string{outputs.TimeKey, "uptime_format"}
)

var (
	MetricCfg       = newMetricConfig(MetricDatabase, MetricTableName, MetricName)
	SecondMetricCfg = newMetricConfig(MetricDatabase, SecondMetricTableName, SecondMetricName)

	MetricTableColumn       = generateMetricTableColumn(MetricColumnNameList, MetricItemSet)
	SecondMetricTableColumn = generateMetricTableColumn(SecondMetricColumnNameList, MetricItemSet)

	MetricPartitionTableConfig = &PartitionTableConfig{
		DatabaseRequest: &api.DatabaseRequest{
			Directory:       MetricCfg.Database,
			PartitionType:   "VALUE",
			PartitionScheme: "2010.01.01..2010.01.30",
			DBHandle:        "example",
		},
		PartitionedTableRequest: &api.CreatePartitionedTableRequest{
			PartitionedTableName: MetricCfg.TableName,
			SrcTable:             DefaultMemTableName,
			PartitionColumns:     strings.Split(MetricCfg.PartitionCol, ","),
		},
		MemTableRequest: generateMemTableRequestForPartition(MetricTableColumn),
	}

	SecondMetricPartitionTableConfig = &PartitionTableConfig{
		DatabaseRequest: &api.DatabaseRequest{
			Directory:       SecondMetricCfg.Database,
			PartitionType:   "VALUE",
			PartitionScheme: "2010.01.01..2010.01.30",
			DBHandle:        "example",
		},
		PartitionedTableRequest: &api.CreatePartitionedTableRequest{
			PartitionedTableName: SecondMetricCfg.TableName,
			SrcTable:             DefaultMemTableName,
			PartitionColumns:     strings.Split(SecondMetricCfg.PartitionCol, ","),
		},
		MemTableRequest: generateMemTableRequestForPartition(SecondMetricTableColumn),
	}
)

type FieldValue struct {
	Value      interface{}
	ErrorValue interface{}
	ValueType  model.DataTypeByte
}

func generateMetricTableColumn(columnNameList []string, itemSet map[string]FieldValue) []TableColumn {
	col := make([]TableColumn, len(columnNameList))

	for k, v := range columnNameList {
		mf, ok := itemSet[v]
		if !ok {
			panic(fmt.Sprintf("invalid col name [%s]", v))
		}

		col[k] = TableColumn{
			Name: v,
			Type: model.GetDataTypeString(mf.ValueType),
		}
	}

	return col
}

func newMetricConfig(dbName, tableName, metricName string) *outputs.Config {
	cfg := *DefaultCfg
	cfg.Database = dbName
	cfg.TableName = tableName
	cfg.MetricName = metricName
	cfg.PartitionCol = MetricColumnNameList[0]

	return &cfg
}

func GenerateMetricEqualToTableColumn() (telegraf.Metric, outputs.TableRow) {
	tagSet := make(map[string]string, 0)
	fieldSet := make(map[string]interface{}, 0)
	tm := MetricItemSet[outputs.TimeKey].Value.(time.Time)
	expectedResult := make(outputs.TableRow, len(MetricColumnNameList))

	for k, v := range MetricTableColumn {
		item, ok := MetricItemSet[v.Name]
		So(ok, ShouldBeTrue)

		dt, err := model.NewDataType(item.ValueType, item.Value)
		So(err, ShouldBeNil)

		expectedResult[k] = dt

		if v.Name == outputs.TimeKey {
			continue
		}

		switch v.Type {
		case model.GetDataTypeString(model.DtString):
			tagSet[v.Name], ok = item.Value.(string)
			So(ok, ShouldBeTrue)
		default:
			fieldSet[v.Name] = item.Value
		}
	}

	m := metric.New(MetricName, tagSet, fieldSet, tm)
	return m, expectedResult
}
