package outputs

import (
	"context"
	"fmt"

	"github.com/dolphindb/api-go/dialer"
	"github.com/dolphindb/api-go/model"
)

type TableSchema struct {
	cfg *Config

	schema *model.Dictionary

	ColumnNameList []string
	ColumnMetaSet  map[string]*ColumnMeta
}

type ColumnMeta struct {
	Index          int
	DataTypeByte   model.DataTypeByte
	DataTypeString string
}

func NewTableSchema(cfg *Config) (*TableSchema, error) {
	var err error

	ts := &TableSchema{cfg: cfg}

	err = ts.fetchSchema()
	if err != nil {
		return nil, fmt.Errorf("fetch schema failed. %v", err)
	}

	err = ts.handleSchemaColumn()
	if err != nil {
		return nil, fmt.Errorf("handle schema column failed. %v", err)
	}

	return ts, nil
}

func (ts *TableSchema) fetchSchema() error {
	conn, err := dialer.NewSimpleConn(context.TODO(), ts.cfg.Address, ts.cfg.User, ts.cfg.Password)
	if err != nil {
		return fmt.Errorf("new simple connection failed. %v", err)
	}

	defer conn.Close()

	df, err := conn.RunScript(ts.generateSchemaScript())
	if err != nil {
		return fmt.Errorf("call function schema for table %s failed. %v", ts.cfg.TableName, err)
	}

	ts.schema = df.(*model.Dictionary)
	return nil
}

func (ts *TableSchema) generateSchemaScript() string {
	if ts.cfg.Database == "" {
		return fmt.Sprintf("schema(%s)", ts.cfg.TableName)
	}

	return fmt.Sprintf("schema(loadTable(\"%s\",\"%s\"))", ts.cfg.Database, ts.cfg.TableName)
}

func (ts *TableSchema) handleSchemaColumn() error {
	dt, err := ts.schema.Get("colDefs")
	if err != nil {
		return fmt.Errorf("get cofDefs failed. %v", err)
	}

	colDefs := dt.Value().(*model.Table)

	colDefsName := colDefs.GetColumnByName("name")
	colDefsTypeInt := colDefs.GetColumnByName("typeInt")

	ts.ColumnNameList = colDefsName.Data.StringList()
	ts.ColumnMetaSet = make(map[string]*ColumnMeta, colDefsTypeInt.Rows())

	for k := range ts.ColumnNameList {
		cm := &ColumnMeta{
			Index:        k,
			DataTypeByte: model.DataTypeByte(colDefsTypeInt.Data.Get(k).Value().(int32)),
		}
		cm.DataTypeString = model.GetDataTypeString(cm.DataTypeByte)

		ts.ColumnMetaSet[ts.ColumnNameList[k]] = cm
	}

	return nil
}
