package ii

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	
	"golib/v3/features/mo"
)

var (
	errUnknownField     = errors.New("unknown field: ")
	errUnknownFieldCall = func(name Name, key string) error {
		return errors.Join(errUnknownField, fmt.Errorf("%s.%s", name, key))
	}
)

func IsUnknownField(err error) bool {
	return errors.Is(err, errUnknownField)
}

// ItemInfo XML 配置, 每个 XML 应当包含 _id 字段
type ItemInfo struct {
	Name   Name        `xml:"Name,attr"`
	Label  string      `xml:"Label,attr"`
	Fields []FieldInfo `xml:"Fields>Field"`
	
	fieldMap    map[string]int
	requiredMap map[string]int // 必填
	uniqueMap   map[string]int // 需要调用 SetUnique 设置唯一键
}

func (c *ItemInfo) ForkDb(name string) Name {
	return Name(c.Name.Database() + "." + name)
}

func (c *ItemInfo) IsRequired(field string) bool {
	_, ok := c.requiredMap[field]
	return ok
}

func (c *ItemInfo) IsUnique(field string) bool {
	_, ok := c.uniqueMap[field]
	return ok
}

func (c *ItemInfo) RequiredCheck(doc mo.M) error {
	for key := range c.requiredMap {
		if _, ok := doc[key]; !ok {
			return errRequired(key, doc)
		}
	}
	return nil
}

func (c *ItemInfo) CopyMap(doc mo.M) (mo.M, error) {
	m := make(mo.M)
	for key, val := range doc {
		switch key {
		case ID, Creator, CreationTime, LastModified, LastUpdater:
			continue
		}
		field, ok := c.Field(key)
		if !ok {
			continue
		}
		v, err := field.Convert(val)
		if err != nil {
			return nil, err
		}
		m[key] = v
	}
	return mo.DeepCopy(m)
}

// prepareInsert 创一个列表, 包含所有 Fields 的 name 和默认值
func (c *ItemInfo) prepareInsert(doc mo.M) {
	for _, field := range c.Fields {
		if _, ok := doc[field.Name]; ok {
			continue
		}
		if field.Name == mo.OID && !field.Required {
			continue // 当 XML 配置了 _id 但是并未指定 Required 通常用于捕捉 _id 而不是将其参与计算
		}
		doc[field.Name] = field.DefaultValue()
	}
}

// PrepareInsert 准备插入的数据
func (c *ItemInfo) PrepareInsert(doc mo.M, u User) error {
	for key, val := range doc {
		field, ok := c.Field(key)
		if !ok {
			// 特殊处理 _id
			if key == mo.OID {
				if oid, ok := val.(mo.ObjectID); !(ok && !oid.IsZero()) {
					return fmt.Errorf("invalid ObjectID: %s(%v)", reflect.TypeOf(val), val)
				}
			}
			// 不允许添加配置文件中不存在的字段
			return errUnknownFieldCall(c.Name, key)
		}
		v, err := field.ConvertWithValidate(val)
		if err != nil {
			return err
		}
		doc[field.Name] = v
	}
	// 填充配置文件中已存在的字段
	c.prepareInsert(doc)
	// 校验必填, TODO 可能不会起作用
	if err := c.RequiredCheck(doc); err != nil {
		return err
	}
	if u != nil {
		doc[Creator] = u.ID()
	}
	doc[CreationTime] = mo.NewDateTime()
	return nil
}

func (c *ItemInfo) prepareUpdateObject(k string, v any) (any, error) {
	fieldName, subFieldName, ok := strings.Cut(k, ".")
	if !ok {
		return nil, errUnknownFieldCall(c.Name, k)
	}
	field, fo := c.Field(fieldName)
	if !fo {
		return nil, errUnknownFieldCall(c.Name, fieldName)
	}
	if field.Type != mo.TypeObject {
		return nil, errTypeReturn(&field, v)
	}
	subField, so := field.SubField(subFieldName)
	if !so {
		return nil, errUnknownFieldCall(c.Name, k)
	}
	return subField.ConvertWithValidate(v)
}

func (c *ItemInfo) prepareUpdateArray(k string, v any) (any, error) {
	name := strings.Split(k, ".")
	if len(name) < 2 {
		return nil, errUnknownFieldCall(c.Name, k)
	}
	fieldName := name[0]
	field, ok := c.Field(fieldName)
	if !ok {
		return nil, errUnknownFieldCall(c.Name, fieldName)
	}
	if field.Type != mo.TypeArray {
		return nil, errTypeReturn(&field, v)
	}
	if field.Items == FieldItemsObject {
		if len(name) != 3 {
			return nil, errUnknownFieldCall(c.Name, k)
		}
		subFieldName := name[2]
		subField, o := field.SubField(subFieldName)
		if !o {
			return nil, errUnknownFieldCall(c.Name, fieldName+"."+subFieldName)
		}
		return subField.ConvertWithValidate(v)
	} else {
		return field.ConvertWithValidate(v)
	}
}

func (c *ItemInfo) PrepareUpdater(updater mo.D, u User) error {
	hasSetter := false
	for i, ele := range updater {
		switch ele.Key {
		case mo.PoSet:
			doc, err := mo.ToM(ele.Value.(mo.D))
			if err != nil {
				return err
			}
			if err = c.PrepareUpdate(doc); err != nil {
				return err
			}
			if ele.Key == mo.PoSet {
				if u != nil {
					doc[LastUpdater] = u.ID()
				}
				doc[LastModified] = mo.NewDateTime()
				hasSetter = true
			}
			update, err := mo.ToD(doc)
			if err != nil {
				return err
			}
			updater[i] = mo.E{Key: ele.Key, Value: update}
		case mo.PoSetOnInsert:
			date := ele.Value.(mo.D)
			date = append(date, mo.E{Key: CreationTime, Value: mo.NewDateTime()})
			updater[i] = mo.E{Key: ele.Key, Value: date}
			hasSetter = true
		default:
			for _, ev := range ele.Value.(mo.D) {
				// 对于非 mo.PoSet 类型的更新, 仅判断字段是否存在, 不再为其检测和转换数据类型
				if _, ok := c.Field(ev.Key); !ok {
					return errUnknownFieldCall(c.Name, ev.Key)
				}
			}
		}
	}
	if !hasSetter {
		var d mo.D
		if u != nil {
			d = append(d, mo.E{
				Key:   LastUpdater,
				Value: u.ID(),
			})
		}
		d = append(d, mo.E{
			Key:   LastModified,
			Value: mo.NewDateTime(),
		})
		updater = append(mo.D{{Key: mo.PoSet, Value: d}}, updater...)
	}
	return nil
}

// PrepareUpdate 准备更新的数据
func (c *ItemInfo) PrepareUpdate(doc mo.M) error {
	for k, v := range doc {
		if k == mo.OID {
			return fmt.Errorf("_id value can not be update")
		}
		var err error
		field, ok := c.Field(k)
		if !ok {
			switch strings.Count(k, ".") {
			case 1:
				if v, err = c.prepareUpdateObject(k, v); err != nil {
					return err
				}
			case 2:
				if v, err = c.prepareUpdateArray(k, v); err != nil {
					return err
				}
			default:
				return errUnknownFieldCall(c.Name, k)
			}
		} else {
			v, err = field.ConvertWithValidate(v)
			if err != nil {
				return err
			}
		}
		doc[k] = v
	}
	return nil
}

// QueryFilterCheck 检查 key 是否包在 itemName 中, 防止 SQL 注入
func (c *ItemInfo) QueryFilterCheck(filter mo.D) error {
	for _, ele := range filter {
		if strings.ContainsRune(ele.Key, '.') { // 检查子 map 查找
			mainKey, subKey, _ := strings.Cut(ele.Key, ".")
			mainField, found := c.Field(mainKey)
			if !found {
				return errUnknownFieldCall(c.Name, mainKey)
			}
			_, found = mainField.SubField(subKey)
			if !found {
				return errUnknownFieldCall(c.Name, ele.Key)
			}
		} else {
			if _, found := c.Field(ele.Key); !found {
				return errUnknownFieldCall(c.Name, ele.Key)
			}
		}
	}
	return nil
}

// ResolveFilter 从 params 中根据 itemInfo 解析出已知的 filed 与未知的 filed
// 对于已知的 filed 会将其值转换为对应类型的数据
func (c *ItemInfo) ResolveFilter(params mo.D) (known, unknown mo.D, err error) {
	for _, ele := range params {
		field, found := c.Field(ele.Key)
		if !found {
			unknown = append(unknown, ele)
			continue
		}
		val, err := field.Convert(ele.Value)
		if err != nil {
			return nil, nil, err
		}
		known = append(known, mo.E{Key: ele.Key, Value: val})
	}
	return
}

func (c *ItemInfo) Field(name string) (FieldInfo, bool) {
	if field, ok := internalField[name]; ok {
		if idx, o := c.fieldMap[name]; o {
			return c.Fields[idx], true
		}
		return field, true
	}
	idx, ok := c.fieldMap[name]
	if !ok {
		return FieldInfo{}, false
	}
	return c.Fields[idx], true
}

func (c *ItemInfo) FieldType(t mo.Type) []FieldInfo {
	fields := make([]FieldInfo, 0)
	for _, field := range c.Fields {
		if field.Type == t {
			fields = append(fields, field)
		}
	}
	return fields
}

func (c *ItemInfo) getUniques() (ks []string) {
	for k := range c.uniqueMap {
		ks = append(ks, k)
	}
	for _, field := range c.Fields {
		for k := range field.uniqueMap {
			ks = append(ks, k)
		}
	}
	return ks
}
