package i2es

import (
	"encoding/json"
	"errors"
	"strings"

	"github.com/google/uuid"
	log "github.com/sirupsen/logrus"
)

// getorcreate topicID. Generate new unique topicID if message is start message
func (t topicid) getOrCreate(msg *ESDoc) {
	if msg.TopicID == "" && msg.Repto == "" {
		log.Warn("New topic assign for message " + msg.MsgID)
		msg.TopicID = uuid.New().String()
	}
}

// AssignTopics ...
func (es ESConf) AssignTopics(messages []ESDoc) error {
	log.Info("====\nTrying assign topics\n====")

	for _, message := range messages {
		log.Info("Working for " + message.MsgID)
		// generate new topicid
		if message.Repto == "" && message.TopicID == "" {
			topic := uuid.New().String()
			message.TopicID = topic
			_, err := es.PutToIndex(message)
			if err != nil {
				log.Error(err.Error())
				continue
			}
			log.Info("New topic " + topic + " for message " + message.MsgID + " assigned")
		}

		// skip message with assigned topicid
		if message.TopicID != "" {
			log.Info("message " + message.MsgID + " has already topicid " + message.TopicID)
			continue
		}

		// find top message of the thread and get they topicid
		if message.Repto != "" {
			log.Info("Repto " + message.Repto + " found in message " + message.MsgID)
			t := topicid{es}
			topicid, err := t.findTopicID(message.Repto, 0)
			if err != nil {
				log.Error(err.Error())
				message.Misplaced = "yes"
				log.Error("Message " + message.MsgID + " misplaced")
				_, err := es.PutToIndex(message)
				if err != nil {
					log.Error(err.Error())
					continue
				}
				continue
			}

			message.TopicID = topicid
			_, err = es.PutToIndex(message)
			if err != nil {
				log.Error(err.Error())
				continue
			}
			log.Info("Topic " + topicid + "found for message " + message.MsgID)
		}
	}
	return nil
}

// findTopicID for msgid
func (t topicid) findTopicID(msgid string, enter int) (string, error) {
	searchURI := strings.Join([]string{t.es.Host, t.es.Index, t.es.Type, "_search"}, "/")
	searchQ := strings.Join([]string{`{"sort": [{"date": {"order": "desc"}}, {"_score": { "order": "desc" }}], "query": { "query_string" : {"fields": ["msgid"], "query": "msgid.keyword:` + msgid + ` "}}}`}, "")
	log.Info(searchQ)

	body, err := doRequest(searchURI, searchQ, "POST")
	if err != nil {
		return "", err
	}

	var res ESRes
	err = json.Unmarshal(body, &res)
	if err != nil {
		return "", err
	}

	if res.Hits.Total == 0 {
		e := errors.New("MSGID " + msgid + " not found ")
		log.Error(string(body))
		return "", e
	}

	if res.Hits.Hits[0].Source.TopicID != "" {
		topicid := res.Hits.Hits[0].Source.TopicID
		return topicid, nil
	}

	if res.Hits.Hits[0].Source.Repto != "" && res.Hits.Hits[0].Source.TopicID == "" {
		if enter > 25 {
			topicid := uuid.New().String()
			e := errors.New("Maximum recurse depth. Top message not in index? Forse assign new topicID " + topicid)
			log.Error(e.Error())
			return topicid, err
		} else {
			t.findTopicID(res.Hits.Hits[0].Source.Repto, enter+1)
		}
	} else if res.Hits.Hits[0].Source.Repto == "" && res.Hits.Hits[0].Source.TopicID != "" {
		return res.Hits.Hits[0].Source.TopicID, nil
	}

	if res.Hits.Hits[0].Source.Repto == "" && res.Hits.Hits[0].Source.TopicID == "" {
		// This is a old message without topicid => generate new topicid and update a top document
		topicid := uuid.New().String()
		log.Warn(topicid)
		log.Warn("Top message " + res.Hits.Hits[0].Source.MsgID + " found but it is doesn not have a topicID")
		log.Warn("Generated new topicID " + topicid)
		log.Warn("Update top message")

		updateDoc := res.Hits.Hits[0].Source
		updateDoc.TopicID = topicid

		updateDocPlain, err := json.Marshal(updateDoc)
		if err != nil {
			e := errors.New("Cant serialize document: " + err.Error())
			return "", e
		}

		postURI := strings.Join([]string{t.es.Host, t.es.Index, t.es.Type, updateDoc.MsgID}, "/")
		_, err = doRequest(postURI, string(updateDocPlain), "PUT")
		if err != nil {
			e := errors.New("Cant update document: " + err.Error())
			return "", e
		}

		log.Info(string(updateDocPlain))
		return topicid, nil
	}
	return "", errors.New("Something went wrong")
}

// check if
func (t topicid) checkTopicID(msg *ESDoc) func() string {
	if msg.TopicID != "" {
		return func() string { return msg.TopicID }
	}

	if msg.Repto == "" {
		var genTopicID = func() string {
			topicid := uuid.New().String()
			log.Warn("New start message for topic " + topicid + " found")
			return topicid
		}
		return genTopicID
	}

	if msg.Repto != "" {
		var findAndSetTopicID = func() string {
			topicid, err := t.findTopicID(msg.Repto, 0)
			if err != nil {
				log.Error(err.Error())
				return ""
			}
			log.Info("Topic ID " + topicid + " found for message " + msg.MsgID)
			return topicid
		}
		return findAndSetTopicID
	}

	return func() string { return "" }
}
