package module

import (
	"fmt"
	"math/rand"
	"strconv"
	"strings"

	"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/context"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/log"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/persist/tablestoredb"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/recconf"
)

type UserTopicTableStoreDao struct {
	tablestore     *tablestoredb.TableStore
	userTopicTable string
	topicItemTable string
	configs        map[string]interface{}
	itemType       string
	recallName     string
	recallCount    int
	topicNum       int
}

func NewUserTopicTableStoreDao(config recconf.RecallConfig) *UserTopicTableStoreDao {
	dao := &UserTopicTableStoreDao{
		itemType:       config.ItemType,
		recallName:     config.Name,
		recallCount:    1000,
		topicNum:       5,
		userTopicTable: config.UserTopicDaoConf.UserTopicTable,
		topicItemTable: config.UserTopicDaoConf.TopicItemTable,
	}
	tablestore, err := tablestoredb.GetTableStore(config.UserTopicDaoConf.TableStoreName)
	if err != nil {
		log.Error(fmt.Sprintf("%v", err))
		return nil
	}
	dao.tablestore = tablestore
	return dao
}

func (d *UserTopicTableStoreDao) ListItemsByUser(user *User, context *context.RecommendContext) (ret []*Item) {
	// uid := string(user.Id)
	getRowRequest := new(tablestore.GetRowRequest)
	criteria := new(tablestore.SingleRowQueryCriteria)
	putPk := new(tablestore.PrimaryKey)
	putPk.AddPrimaryKeyColumn("user_id", string(user.Id))

	criteria.PrimaryKey = putPk
	criteria.ColumnsToGet = []string{"favorite_topic"}
	getRowRequest.SingleRowQueryCriteria = criteria
	getRowRequest.SingleRowQueryCriteria.TableName = d.userTopicTable
	getRowRequest.SingleRowQueryCriteria.MaxVersion = 1
	getResp, err := d.tablestore.Client.GetRow(getRowRequest)
	if err != nil {
		log.Error(fmt.Sprintf("requestId=%s\tmodule=UserTopicTableStoreDao\terror=%v", context.RecommendId, err))
		return
	}
	topics := make([]*topicInfo, 0, d.topicNum)

	if str, ok := getResp.Columns[0].Value.(string); ok {
		idList := strings.Split(str, ",")
		for _, id := range idList {
			tv := strings.Split(id, ":")
			if len(tv) == 2 {
				f, err := strconv.ParseFloat(tv[1], 64)
				if err != nil || f == float64(0) {
					f = float64(0.5)
				}

				info := topicInfo{
					topic: tv[0],
					value: f,
				}
				topics = append(topics, &info)
			} else if len(tv) == 1 {
				info := topicInfo{
					topic: tv[0],
					value: float64(0.5),
				}
				topics = append(topics, &info)
			}
		}
	}

	if len(topics) == 0 {
		log.Info(fmt.Sprintf("module=UserTopicTableStoreDao\tuid=%s\terr=topic empty", user.Id))
		return
	}

	if len(topics) > d.topicNum {
		rand.Shuffle(len(topics), func(i, j int) {
			topics[i], topics[j] = topics[j], topics[i]
		})

		topics = topics[:d.topicNum]
	}
	totalValue := float64(0)
	for _, topic := range topics {
		totalValue += topic.value
	}

	for _, topic := range topics {
		topic.count = int((topic.value / totalValue) * float64(d.recallCount))
		result, err := func(info *topicInfo) ([]*Item, error) {
			result := make([]*Item, 0)

			getRangeRequest := &tablestore.GetRangeRequest{}
			rangeRowQueryCriteria := &tablestore.RangeRowQueryCriteria{}
			rangeRowQueryCriteria.TableName = d.topicItemTable

			startPK := new(tablestore.PrimaryKey)
			startPK.AddPrimaryKeyColumn("topic", info.topic)
			startPK.AddPrimaryKeyColumnWithMinValue("auto_id")
			endPK := new(tablestore.PrimaryKey)
			endPK.AddPrimaryKeyColumn("topic", info.topic)
			endPK.AddPrimaryKeyColumnWithMaxValue("auto_id")
			rangeRowQueryCriteria.StartPrimaryKey = startPK
			rangeRowQueryCriteria.EndPrimaryKey = endPK
			rangeRowQueryCriteria.ColumnsToGet = []string{"item_id"}
			rangeRowQueryCriteria.Direction = tablestore.FORWARD
			rangeRowQueryCriteria.MaxVersion = 1
			rangeRowQueryCriteria.Limit = int32(info.count)
			getRangeRequest.RangeRowQueryCriteria = rangeRowQueryCriteria

			getRangeResp, err := d.tablestore.Client.GetRange(getRangeRequest)
			if err != nil {
				log.Error(fmt.Sprintf("requestId=%s\tmodule=UserTopicTableStoreDao\terror=%v", context.RecommendId, err))
				return result, err
			}
			for _, row := range getRangeResp.Rows {
				item := &Item{
					Id:         ItemId(row.Columns[0].Value.(string)),
					ItemType:   d.itemType,
					RetrieveId: d.recallName,
				}
				result = append(result, item)
			}

			return result, nil
		}(topic)

		if err == nil {
			ret = append(ret, result...)
		}

	}

	return
}
