package dao

import (
	"reflect"
	"time"

	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/globals"
	"git.rosy.net.cn/jx-callback/globals/refutil"
)

type KVUpdateItem struct {
	Item interface{}
	KVs  map[string]interface{}
}

// 这里面的函数要求实体是IDCUDL的，即含有ID, UpdatedAt, LastOperator, DeletedAt字段

func GetEntitiesByKV(db *DaoDB, item interface{}, conditions map[string]interface{}, isIncludeDeleted bool) (err error) {
	if db == nil {
		db = GetDB()
	}
	err = utils.CallFuncLogError(func() error {
		typeInfo := reflect.TypeOf(item).Elem().Elem()
		if typeInfo.Kind() == reflect.Ptr {
			typeInfo = typeInfo.Elem()
		}
		qs := db.Db.QueryTable(reflect.New(typeInfo).Interface())
		for k, v := range conditions {
			qs = qs.Filter(k, v)
		}
		qs = qs.Limit(-1)
		if !isIncludeDeleted {
			if _, ok := typeInfo.FieldByName(model.FieldDeletedAt); ok {
				qs = qs.Filter(model.FieldDeletedAt, utils.DefaultTimeValue)
			}
		}
		_, err = qs.Limit(-1).All(item)
		return err
	}, reflect.TypeOf(item).Name())
	return err
}

func UpdateEntityByKV(db *DaoDB, item interface{}, kvs map[string]interface{}, conditions map[string]interface{}) (num int64, err error) {
	if db == nil {
		db = GetDB()
	}
	qs := db.Db.QueryTable(item)
	if conditions == nil {
		qs = qs.Filter(model.FieldID, refutil.GetObjFieldByName(item, model.FieldID))
	} else {
		for k, v := range conditions {
			qs = qs.Filter(k, v)
		}
	}
	for k, v := range kvs {
		if k == "jdCategoryID" && (v == "" || v == nil) {
			delete(kvs, k)
		}
	}
	num, err = qs.Update(kvs)
	if err != nil && !IsDuplicateError(err) {
		globals.SugarLogger.Errorf("UpdateEntityByKV %s failed with error:%v", reflect.TypeOf(item).Name(), err)
	}
	return num, err
}

func BatchUpdateEntityByKV(db *DaoDB, items []*KVUpdateItem) (num int64, err error) {
	if len(items) > 0 {
		// 不用事务应该是OK的吧，用事务经常会导致死锁（不同平台在同步同一条数据）
		// Begin(db)
		// defer func() {
		// 	if r := recover(); r != nil || err != nil {
		// 		Rollback(db)
		// 		if r != nil {
		// 			panic(r)
		// 		}
		// 	}
		// }()

		var item *KVUpdateItem
		defer func() {
			if r := recover(); r != nil {
				if item != nil {
					var bindID int64
					value := refutil.CheckAndGetStructValue(item.Item).FieldByName(model.FieldID)
					if value.IsValid() {
						bindID = value.Int()
					}
					globals.SugarLogger.Debugf("BatchUpdateEntityByKV panic, bindID:%d, KVs:%s", bindID, utils.Format4Output(item.KVs, true))
				}
				panic(r)
			}
		}()

		for _, item = range items {
			num2, err2 := UpdateEntityByKV(db, item.Item, item.KVs, nil)
			if err = err2; err != nil {
				return 0, err
			}
			num += num2
		}
		// Commit(db)
	}
	return num, nil
}

func UpdateEntityLogically(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}) (num int64, err error) {
	if conditions != nil && refutil.IsFieldExist(item, model.FieldDeletedAt) {
		conditions = utils.MergeMaps(conditions, map[string]interface{}{
			model.FieldDeletedAt: utils.DefaultTimeValue,
		})
	}
	params := map[string]interface{}{
		model.FieldUpdatedAt: time.Now(),
	}
	if userName != "" {
		params[model.FieldLastOperator] = userName
	}
	return UpdateEntityByKV(db, item, utils.MergeMaps(kvs, params), conditions)
}

// 此函数会更新同步标志
func UpdateEntityLogicallyAndUpdateSyncStatus(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}, syncStatusFieldName string, valueMask int) (num int64, err error) {
	if conditions != nil && refutil.IsFieldExist(item, model.FieldDeletedAt) {
		conditions = utils.MergeMaps(conditions, map[string]interface{}{
			model.FieldDeletedAt: utils.DefaultTimeValue,
		})
	} else {
		conditions = map[string]interface{}{
			model.FieldID: refutil.GetObjFieldByName(item, model.FieldID),
		}
	}
	typeInfo := reflect.TypeOf(item)
	valueRows := reflect.New(reflect.SliceOf(typeInfo))
	rows := valueRows.Interface()
	if err = GetEntitiesByKV(db, rows, conditions, false); err == nil {
		valueRows = reflect.Indirect(valueRows)
		for i := 0; i < valueRows.Len(); i++ {
			value := reflect.Indirect(valueRows.Index(i))
			status := value.FieldByName(syncStatusFieldName).Int() | int64(valueMask)
			params := map[string]interface{}{
				model.FieldUpdatedAt: time.Now(),
				syncStatusFieldName:  status,
			}
			if userName != "" {
				params[model.FieldLastOperator] = userName
			}
			num2, err2 := UpdateEntityByKV(db, value.Interface(), utils.MergeMaps(kvs, params), nil)
			if err = err2; err == nil {
				num += num2
			} else {
				return num, err
			}
		}
	}
	return num, err
}

func DeleteEntityLogically(db *DaoDB, item interface{}, kvs map[string]interface{}, userName string, conditions map[string]interface{}) (num int64, err error) {
	return UpdateEntityLogically(db, item, utils.MergeMaps(kvs, map[string]interface{}{
		model.FieldDeletedAt: time.Now(),
	}), userName, conditions)
}
