package ii

import (
	"errors"
	"fmt"
	"slices"
	"strings"

	"git.simanc.com/software/golib/v4/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
	uniqueMap map[string]int // 需要调用 SetUnique 设置唯一键
}

func (c *ItemInfo) ForkDb(name string) Name {
	return NewName(c.Name.DbName(), name)
}

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

// prepareInsert 创一个列表, 包含所有 Fields 的 name 和默认值
func (c *ItemInfo) prepareInsert(doc mo.D) mo.D {
	for _, field := range c.Fields {
		idx := slices.IndexFunc(doc, func(ele mo.E) bool {
			if ele.Key == field.Name {
				return true
			}
			return false
		})
		if idx == -1 {
			doc = append(doc, mo.E{Key: field.Name, Value: field.DefaultValue()})
			continue
		}
		if field.Name == mo.OID {
			if id, ok := doc[idx].Value.(mo.ObjectID); ok && !id.IsZero() {
				continue // 如果指定了 _id 并且 _id 有效
			}
			doc = append(doc, mo.E{Key: field.Name, Value: field.DefaultValue()})
		}
	}
	return doc
}

// PrepareInsert 准备插入的数据
func (c *ItemInfo) PrepareInsert(doc mo.D, u User) (mo.D, error) {
	for i, ele := range doc {
		field, ok := c.Field(ele.Key)
		if !ok {
			// 不允许添加配置文件中不存在的字段
			return nil, errUnknownFieldCall(c.Name, ele.Key)
		}
		v, err := field.ConvertWithValidate(ele.Value)
		if err != nil {
			return nil, err
		}
		doc[i] = mo.E{Key: field.Name, Value: v}
	}
	// 填充配置文件中已存在的字段
	doc = c.prepareInsert(doc)
	if u != nil {
		doc = append(doc, mo.E{Key: Creator, Value: u.ID()})
	}
	doc = append(doc, mo.E{Key: CreationTime, Value: mo.NewDateTime()})
	return doc, nil
}

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

func (c *ItemInfo) prepareUpdateArray(e *mo.E) (any, error) {
	name := strings.Split(e.Key, ".")
	if len(name) < 2 {
		return nil, errUnknownFieldCall(c.Name, e.Key)
	}
	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, e.Value)
	}
	if field.Items == FieldItemsObject {
		if len(name) != 3 {
			return nil, errUnknownFieldCall(c.Name, e.Key)
		}
		subFieldName := name[2]
		subField, o := field.SubField(subFieldName)
		if !o {
			return nil, errUnknownFieldCall(c.Name, fieldName+"."+subFieldName)
		}
		return subField.ConvertWithValidate(e.Value)
	} else {
		return field.ConvertWithValidate(e.Value)
	}
}

func (c *ItemInfo) PrepareUpdater(updater mo.D, u User) error {
	hasSetter := false
	for i, ele := range updater {
		switch ele.Key {
		case mo.OptSet:
			resolved, err := c.PrepareUpdate(ele.Value.(mo.D))
			if err != nil {
				return err
			}
			updater[i] = mo.E{Key: ele.Key, Value: c.appendUpdateField(resolved, u)}
			hasSetter = true
		case mo.OptSetOnInsert:
			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.OptSet 类型的更新, 仅判断字段是否存在, 不再为其检测和转换数据类型
				if _, ok := c.Field(ev.Key); !ok {
					return errUnknownFieldCall(c.Name, ev.Key)
				}
			}
		}
	}
	if !hasSetter {
		updater = append(mo.D{{
			Key:   mo.OptSet,
			Value: c.appendUpdateField(mo.D{}, u)}},
			updater...,
		)
	}
	return nil
}

// PrepareUpdate 准备更新的数据
func (c *ItemInfo) PrepareUpdate(doc mo.D) (mo.D, error) {
	var resolved mo.D
	for _, e := range doc {
		if e.Key == mo.OID {
			return nil, fmt.Errorf("_id value can not be update")
		}
		field, ok := c.Field(e.Key)
		if !ok {
			switch strings.Count(e.Key, ".") {
			case 1:
				v, err := c.prepareUpdateObject(&e)
				if err != nil {
					return nil, err
				}
				resolved = append(resolved, mo.E{Key: e.Key, Value: v})
			case 2:
				v, err := c.prepareUpdateArray(&e)
				if err != nil {
					return nil, err
				}
				resolved = append(resolved, mo.E{Key: e.Key, Value: v})
			default:
				return nil, errUnknownFieldCall(c.Name, e.Key)
			}
		} else {
			if field.ReadOnly {
				continue
			}
			v, err := field.ConvertWithValidate(e.Value)
			if err != nil {
				return nil, err
			}
			resolved = append(resolved, mo.E{Key: e.Key, Value: v})
		}
	}
	return resolved, nil
}

func (c *ItemInfo) appendUpdateField(resolved mo.D, u User) mo.D {
	if u != nil {
		return append(resolved, mo.E{Key: LastUpdater, Value: u.ID()})
	}
	return append(resolved, mo.E{Key: LastModified, Value: mo.NewDateTime()})
}

// 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
}
