package domain

import (
	"fmt"
	"github.com/linmadan/egglib-go/utils/xtime"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/utils"
	"strings"
)

// Field 字段
type Field struct {
	// 字段Id
	// FieldId int `json:"fieldId"`
	// 索引序号
	Index int `json:"index"`
	// 名称
	Name string `json:"name"`
	// 对应数据库名称
	SQLName string `json:"sqlName"`
	// 对应数据库类型
	SQLType string `json:"sqlType"`
	// 描述
	Description string `json:"description"`
	// 标识 1.主键 2:主表字段 3:手动添加
	Flag int `json:"flag"`
}

func (f *Field) Valid() error {
	if _, ok := SQLTypeMap[strings.ToUpper(f.SQLType)]; !ok {
		return fmt.Errorf("unknown sql type:%v", f.SQLType)
	}
	if f.Index == 0 && f.Flag == ManualField {
		return fmt.Errorf("field:%v index is 0", f.Name)
	}
	return nil
}

func (f *Field) Copy() *Field {
	return &Field{
		Index:   f.Index,
		Name:    f.Name,
		SQLName: f.SQLName,
		SQLType: f.SQLType,
		Flag:    f.Flag,
	}
}

type Fields []*Field

func (fields Fields) ToMap() map[string]*Field {
	m := make(map[string]*Field)
	for i := range fields {
		m[fields[i].Name] = fields[i]
	}
	return m
}

func (fields Fields) ToMapBySqlName() map[string]*Field {
	m := make(map[string]*Field)
	for i := range fields {
		m[fields[i].SQLName] = fields[i]
	}
	return m
}

func (fields Fields) NameArrayString() []string {
	m := make([]string, 0)
	for i := range fields {
		m = append(m, fields[i].Name)
	}
	return m
}

func (fields Fields) Select(options map[string]interface{}) []*Field {
	var result []*Field
	for _, field := range fields {
		if v, ok := options["flag"]; ok {
			if v.(int) != field.Flag {
				continue
			}
		}
		result = append(result, field)
	}
	return result
}

func ValidFields(fields []*Field) error {
	m := (Fields)(fields).ToMap()
	if len(m) != len(fields) {
		return fmt.Errorf("列名重复发")
	}

	for _, f := range fields {
		if err := f.Valid(); err != nil {
			return err
		}
	}
	return nil
}

func FieldsChange(oldFields []*Field, newFields []*Field) (reserve []*Field, delete []*Field, add []*Field) {
	var oldFieldsMap = (Fields)(oldFields).ToMap()
	var newFieldsMap = (Fields)(newFields).ToMap()

	for _, f := range newFields {
		if _, ok := oldFieldsMap[f.Name]; ok {
			reserve = append(reserve, f)
			continue
		} else {
			add = append(add, f)
			continue
		}
	}

	for _, f := range oldFields {
		if _, ok := newFieldsMap[f.Name]; !ok {
			delete = append(delete, f)
			continue
		}
	}
	return
}

type FieldValues struct {
	Number      int           `json:"rowIndex"`
	FieldValues []*FieldValue `json:"fieldValues"`
}

func (f *FieldValues) Valid() error {
	for _, item := range f.FieldValues {
		if err := item.Valid(); err != nil {
			return err
		}
	}
	return nil
}

type FieldValue struct {
	*Field
	// 字段值(当前)
	Value string `json:"value,omitempty"`
	// 字段值(旧的,更新时有效)
	OldValue string `json:"oldValue,omitempty"`

	typeValue interface{}
}

func (f *FieldValue) CheckValue() error {
	val, err := ValueToType(f.Value, f.SQLType)
	if err != nil {
		return err
	}
	f.typeValue = val
	return nil
}

func (f *FieldValue) TypeValue() interface{} {
	if f.typeValue == nil {
		f.typeValue, _ = ValueToType(f.Value, f.SQLType)
	}
	return f.typeValue
}

func ValueToType(value string, sqlType string) (interface{}, error) {
	var toTypeVal interface{}
	var err error
	numberString := utils.NewNumberString(value)
	switch sqlType {
	case String.ToString():
		toTypeVal = value
	case Int.ToString():
		toTypeVal, err = numberString.Int()
		if err != nil {
			err = fmt.Errorf("[%v]不是有效的数值类型", value)
		}
	case BigInt.ToString():
		toTypeVal, err = numberString.Int()
		if err != nil {
			err = fmt.Errorf("[%v]不是有效的数值类型", value)
		}
	case Float.ToString():
		toTypeVal, err = numberString.Float64()
		if err != nil {
			err = fmt.Errorf("[%v]不是有效的浮点数类型", value)
		}
	case Date.ToString():
		toTypeVal, err = xtime.Parse(value)
		if err != nil {
			err = fmt.Errorf("[%v]不是有效的日期类型", value)
		}
		toTypeVal = value
	case Datetime.ToString():
		toTypeVal, err = xtime.Parse(value)
		if err != nil {
			err = fmt.Errorf("[%v]不是有效的时间类型", value)
		}
		toTypeVal = value
	default:
		return nil, fmt.Errorf("unknow sql type :%v", sqlType)
	}
	return toTypeVal, err
}

func ToFieldData(fields []*Field, data [][]string, byName bool) []map[string]string {
	var result = make([]map[string]string, 0)
	var key string
	for _, d := range data {
		var item = make(map[string]string)
		for j, f := range fields {
			key = f.SQLName
			if byName {
				key = f.Name
			}
			if len(d) >= j {
				item[key] = d[j]
			} else {
				item[key] = ""
			}
		}
		result = append(result, item)
	}
	return result
}

func GripData(data []map[string]string, total int64) map[string]interface{} {
	if len(data) == 0 {
		data = make([]map[string]string, 0)
	}
	return map[string]interface{}{
		"list":  data,
		"total": total,
	}
}

func PK() *Field {
	return &Field{
		Index:       0,
		Name:        "序号",
		SQLName:     "id",
		SQLType:     String.ToString(),
		Description: "主键",
		Flag:        PKField,
	}
}

func MakeToInterfaces(fields []*Field) func([]string) []interface{} {
	return func(input []string) []interface{} {
		output := make([]interface{}, len(input))
		for i, v := range input {
			if i < len(fields) {
				convValue, err := ValueToType(v, fields[i].SQLType)
				if err == nil {
					output[i] = convValue
					continue
				}
			}
			output[i] = v
		}
		return output
	}
}