package module

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
)

type ItemId string

type Item struct {
	Id         ItemId `json:"id"`
	Name       string `json:"name,omitempty"`
	Score      float64
	RetrieveId string
	ItemType   string
	// ItemStatus int
	Embedding []float64

	mutex sync.RWMutex
	// TimeStamp  time.Time
	Properties map[string]interface{} `json:"properties"`
	algoScores map[string]float64
}

func NewItem(id string) *Item {
	item := Item{
		Id:         ItemId(id),
		Score:      0,
		Properties: make(map[string]interface{}, 32),
	}
	//item.algoScores = make(map[string]float64)
	return &item
}
func NewItemWithProperty(id string, properties map[string]interface{}) *Item {
	item := NewItem(id)
	for k, v := range properties {
		item.Properties[k] = v
	}

	return item
}
func (t *Item) GetRecallName() string {
	return t.RetrieveId
}
func (t *Item) GetAlgoScores() map[string]float64 {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	return t.algoScores
}
func (t *Item) AddProperty(key string, value interface{}) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.Properties[key] = value
}
func (t *Item) GetProperty(key string) interface{} {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	val, ok := t.Properties[key]
	if !ok {
		return nil
	}
	return val
}
func (t *Item) StringProperty(key string) string {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	val, ok := t.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))
	case int32:
		return strconv.Itoa(int(value))
	case int64:
		return strconv.Itoa(int(value))
	}
	return ""
}
func (t *Item) FloatProperty(key string) (float64, error) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	val, ok := t.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 (t *Item) IntProperty(key string) (int, error) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	val, ok := t.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 (t *Item) AddAlgoScore(name string, score float64) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if t.algoScores == nil {
		t.algoScores = make(map[string]float64)
	}
	t.algoScores[name] = score
}
func (t *Item) FloatExprData(name string) (float64, error) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	if name == "current_score" {
		return t.Score, nil
	}
	val, ok := t.algoScores[name]
	if ok {
		return val, nil
	}

	return float64(0), errors.New(fmt.Sprintf("not found,name:%s", name))
}
func (t *Item) GetFeatures() map[string]interface{} {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if t.RetrieveId != "" {
		if _, ok := t.Properties[t.RetrieveId]; !ok {
			t.Properties[t.RetrieveId] = t.Score
			t.Properties["recall_name"] = t.RetrieveId
			t.Properties["recall_score"] = t.Score
		}
	}

	features := make(map[string]interface{}, len(t.Properties))

	for k, v := range t.Properties {
		features[k] = v
	}

	return features
}
func (t *Item) DeleteProperty(key string) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	delete(t.Properties, key)
}
func (t *Item) DeleteProperties(features []string) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	for _, key := range features {
		delete(t.Properties, key)
	}
}
func (t *Item) AddProperties(properties map[string]interface{}) {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	for key, val := range properties {
		t.Properties[key] = val
	}
}
func (t *Item) DeepClone() *Item {
	item := NewItemWithProperty(string(t.Id), t.Properties)

	item.Score = t.Score
	item.RetrieveId = t.RetrieveId
	item.ItemType = t.ItemType

	return item
}
