package torrentController

import (
	"errors"

	"fmt"
	"net/http"
	"strconv"

	"github.com/NyaaPantsu/nyaa/controllers/router"
	"github.com/NyaaPantsu/nyaa/models"
	"github.com/NyaaPantsu/nyaa/models/tag"
	"github.com/NyaaPantsu/nyaa/models/torrents"
	"github.com/NyaaPantsu/nyaa/templates"
	"github.com/NyaaPantsu/nyaa/utils/api"
	"github.com/NyaaPantsu/nyaa/utils/log"
	msg "github.com/NyaaPantsu/nyaa/utils/messages"
	"github.com/NyaaPantsu/nyaa/utils/validator"
	"github.com/NyaaPantsu/nyaa/utils/validator/tag"
	"github.com/gin-gonic/gin"
)

// postTags is a function used by controllers to post tags
func postTags(c *gin.Context, torrent *models.Torrent, user *models.User) []models.Tag {
	var newTags []models.Tag
	messages := msg.GetMessages(c)
	// Bind already check if a tag is valid or not
	tagsForm := tagsValidator.Bind(c, false)
	if len(tagsForm) == 0 {
		messages.ErrorT(errors.New("wrong_tag_type"))
		return nil
	}
	for _, tagForm := range tagsForm {
		// We validate the tag CreateForm
		validator.ValidateForm(tagForm, messages)
		if messages.HasErrors() { // if there are errors while validating the form, we skip it
			continue
		}

		if len(user.Tags) == 0 { // In case we didn't call userLoadTags before calling this function
			user.LoadTags(torrent)
		}

		newTag := models.Tag{Tag: tagForm.Tag, Type: tagForm.Type, UserID: user.ID, TorrentID: torrent.ID, Weight: user.Pantsu}
		if user.Tags.Contains(newTag) {
			// We check if the user has already submitted this tag, if he has, we prevent him to vote twice for it
			log.Info("User has already tagged the type for the torrent")
			return nil
		}

		// Add a tag to the db and filter them if needed
		// Filtering means that we sum up all the tag with the same type/value
		// and compare the sum with the maximum value (of votes) a tag can have
		// if the value is greater than the maximum, we don't add the tag as a simple vote
		// we add it directly in torrent model as an accepted tag and remove other tags with the same type
		if tags.FilterOrCreate(&newTag, torrent, user) {
			newTags = append(newTags, newTag)
		}

	}
	torrent.Update(false)
	return newTags
}

// ViewFormTag is a controller displaying a form to add a tag to a torrent
func ViewFormTag(c *gin.Context) {
	user := router.GetUser(c)
	// We add a tag only if user logged
	if user.ID > 0 {
		messages := msg.GetMessages(c)
		id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
		// Retrieve the torrent
		torrent, err := torrents.FindByID(uint(id))

		var tagsForm models.Tags
		// If torrent not found, display 404
		if err != nil {
			c.Status(http.StatusNotFound)
			return
		}

		// We load tags for user so we can check if they have them
		user.LoadTags(torrent)
		if c.Request.Method == "POST" {
			tagsForm = postTags(c, torrent, user)
			if _, ok := c.GetQuery("json"); ok {
				apiUtils.ResponseHandler(c, tagsForm)
				return
			}
			if !messages.HasErrors() {
				c.Redirect(http.StatusSeeOther, fmt.Sprintf("/view/%d", id))
			}
		}

		templates.Form(c, "/site/torrents/tag.jet.html", tagsForm)
	}
}

// AddTag is a controller to add a tag
func AddTag(c *gin.Context) {
	messages := msg.GetMessages(c)
	user := router.GetUser(c)
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	// Retrieve the torrent
	torrent, err := torrents.FindByID(uint(id))

	// If torrent not found, display 404
	if err != nil {
		c.Status(http.StatusNotFound)
		return
	}
	tagForm := &tagsValidator.CreateForm{c.Query("tag"), c.Query("type")}
	if c.Query("tag") != "" && user.ID > 0 {
		validator.ValidateForm(tagForm, messages)
		if !messages.HasErrors() {
			// We load tags for user and torrents
			user.LoadTags(torrent)
			newTag := models.Tag{Tag: tagForm.Tag, Type: tagForm.Type, UserID: user.ID, TorrentID: torrent.ID, Weight: user.Pantsu}
			if !user.Tags.Contains(newTag) {
				// We check if the user has already submitted this tag, if he has, we prevent him to vote twice for it
				tags.FilterOrCreate(&newTag, torrent, user)
				torrent.Update(false)
			}
		}
	}
	if _, ok := c.GetQuery("json"); ok {
		apiUtils.ResponseHandler(c, tagForm)
		return
	}
	c.Redirect(http.StatusSeeOther, fmt.Sprintf("/view/%d", id))
}

// DeleteTag is a controller to delete a user tag
func DeleteTag(c *gin.Context) {
	messages := msg.GetMessages(c)
	user := router.GetUser(c)
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	// Retrieve the torrent
	torrent, err := torrents.FindByID(uint(id))

	// If torrent not found, display 404
	if err != nil {
		c.Status(http.StatusNotFound)
		return
	}

	// We load tags for user and torrents
	user.LoadTags(torrent)

	tagForm := &tagsValidator.CreateForm{c.Query("tag"), c.Query("type")}
	if c.Query("tag") != "" && user.ID > 0 {
		validator.ValidateForm(tagForm, messages)

		if !messages.HasErrors() {
			for _, tag := range user.Tags {
				if tag.Tag == tagForm.Tag && tag.Type == tagForm.Type {
					_, err := tag.Delete()
					log.CheckError(err)
					break
				}
			}
		}
	}
	if _, ok := c.GetQuery("json"); ok {
		apiUtils.ResponseHandler(c, tagForm)
		return
	}
	c.Redirect(http.StatusSeeOther, fmt.Sprintf("/view/%d", id))
}
