package module

import (
	"errors"
	"strconv"
	"sync"

	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/context"
)

type User struct {
	Id         UID `json:"uid"`
	Vector     string
	Properties map[string]interface{} `json:"properties"`
	mutex      sync.RWMutex
}

func NewUser(id string) *User {
	user := User{
		Id: UID(id),
	}
	user.Properties = make(map[string]interface{})
	return &user
}
func NewUserWithContext(id UID, context *context.RecommendContext) *User {
	user := NewUser(string(id))

	user.AddProperty("uid", string(id))
	features := context.GetParameter("features")
	if features != nil {
		if _, ok := features.(map[string]interface{}); ok {
			for k, v := range features.(map[string]interface{}) {
				user.AddProperty(k, v)
			}
		}
	}

	return user
}

func (u *User) AddProperty(key string, value interface{}) {
	u.mutex.Lock()
	u.Properties[key] = value
	u.mutex.Unlock()
}
func (u *User) FloatProperty(key string) (float64, error) {
	//u.mutex.RLock()
	//defer u.mutex.RUnlock()
	val, ok := u.Properties[key]
	if !ok {
		return float64(0), errors.New("property key not exist")
	}

	switch value := val.(type) {
	case float64:
		return value, nil
	case int:
		return float64(value), nil
	case string:
		f, err := strconv.ParseFloat(value, 64)
		return f, err
	default:
		return float64(0), errors.New("unspport type")
	}
}

func (u *User) MakeUserFeatures() (features map[string]interface{}) {
	features = make(map[string]interface{})
	for k, v := range u.Properties {
		if k == "type" {
			continue
		}
		if s, ok := v.(float64); ok {
			features[k] = s
			continue
		}
		if str, ok := v.(string); ok {
			if s, err := strconv.ParseFloat(str, 64); err == nil {
				features[k] = s
				continue
			}
		}

		features[k] = v

	}
	return
}

// MakeUserFeatures2 for easyrec processor
func (u *User) MakeUserFeatures2() (features map[string]interface{}) {
	features = make(map[string]interface{}, len(u.Properties))
	for k, v := range u.Properties {
		features[k] = v
	}
	return
}
func (u *User) StringProperty(key string) string {
	u.mutex.RLock()
	defer u.mutex.RUnlock()
	val, ok := u.Properties[key]
	if !ok {
		return ""
	}

	switch value := val.(type) {
	case string:
		return value
	case int:
		return strconv.Itoa(value)
	case float64:
		return strconv.Itoa(int(value))
	}
	return ""
}

func (u *User) DeleteProperty(key string) {
	u.mutex.Lock()
	delete(u.Properties, key)
	u.mutex.Unlock()
}

func (u *User) DeleteProperties(features []string) {
	u.mutex.Lock()
	for _, key := range features {
		delete(u.Properties, key)
	}
	u.mutex.Unlock()
}
func (u *User) IntProperty(key string) (int, error) {
	//u.mutex.RLock()
	//defer u.mutex.RUnlock()
	val, ok := u.Properties[key]
	if !ok {
		return int(0), errors.New("property key not exist")
	}

	switch value := val.(type) {
	case float64:
		return int(value), nil
	case int:
		return value, nil
	case uint:
		return int(value), nil
	case int32:
		return int(value), nil
	case int64:
		return int(value), nil
	case string:
		return strconv.Atoi(value)
	default:
		return int(0), errors.New("unspport type")
	}
}
func (u *User) GetProperty(key string) interface{} {
	//u.mutex.RLock()
	//defer u.mutex.RUnlock()
	val, ok := u.Properties[key]
	if !ok {
		return nil
	}
	return val
}
