package api

import (
	"fmt"
	"net/http"

	"git.kirsle.net/apps/gosocial/pkg/log"
	"git.kirsle.net/apps/gosocial/pkg/models"
	"git.kirsle.net/apps/gosocial/pkg/session"
)

// Likes API.
func Likes() http.HandlerFunc {
	// Request JSON schema.
	type Request struct {
		TableName string `json:"name"`
		TableID   uint64 `json:"id"`
		Unlike    bool   `json:"unlike,omitempty"`
	}

	// Response JSON schema.
	type Response struct {
		OK    bool   `json:"OK"`
		Error string `json:"error,omitempty"`
		Likes int64  `json:"likes"`
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			SendJSON(w, http.StatusNotAcceptable, Response{
				Error: "POST method only",
			})
			return
		}

		// Get the current user.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			SendJSON(w, http.StatusBadRequest, Response{
				Error: "Couldn't get current user!",
			})
			return
		}

		// Parse request payload.
		var req Request
		if err := ParseJSON(r, &req); err != nil {
			SendJSON(w, http.StatusBadRequest, Response{
				Error: fmt.Sprintf("Error with request payload: %s", err),
			})
			return
		}

		// Who do we notify about this like?
		var targetUser *models.User
		switch req.TableName {
		case "photos":
			if photo, err := models.GetPhoto(req.TableID); err == nil {
				if user, err := models.GetUser(photo.UserID); err == nil {
					targetUser = user
				}
			} else {
				log.Error("For like on photos table: didn't find photo %d: %s", req.TableID, err)
			}
		case "users":
			log.Error("subject is users, find %d", req.TableID)
			if user, err := models.GetUser(req.TableID); err == nil {
				targetUser = user
				log.Warn("found user %s", targetUser.Username)
			} else {
				log.Error("For like on users table: didn't find user %d: %s", req.TableID, err)
			}
		}

		// Is the table likeable?
		if _, ok := models.LikeableTables[req.TableName]; !ok {
			SendJSON(w, http.StatusBadRequest, Response{
				Error: fmt.Sprintf("Can't like table %s: not allowed.", req.TableName),
			})
			return
		}

		// Put in a like.
		if req.Unlike {
			if err := models.Unlike(currentUser, req.TableName, req.TableID); err != nil {
				SendJSON(w, http.StatusBadRequest, Response{
					Error: fmt.Sprintf("Error unliking: %s", err),
				})
				return
			}

			// Remove the target's notification about this like.
			models.RemoveNotification(req.TableName, req.TableID)
		} else {
			if err := models.AddLike(currentUser, req.TableName, req.TableID); err != nil {
				SendJSON(w, http.StatusBadRequest, Response{
					Error: fmt.Sprintf("Error liking: %s", err),
				})
				return
			}

			// Notify the recipient of the like.
			log.Info("Added like on %s:%d, notifying owner %+v", req.TableName, req.TableID, targetUser)
			if targetUser != nil {
				notif := &models.Notification{
					UserID:    targetUser.ID,
					User:      *currentUser,
					Type:      models.NotificationLike,
					TableName: req.TableName,
					TableID:   req.TableID,
				}
				if err := models.CreateNotification(notif); err != nil {
					log.Error("Couldn't create Likes notification: %s", err)
				}
			}
		}

		// Send success response.
		SendJSON(w, http.StatusOK, Response{
			OK:    true,
			Likes: models.CountLikes(req.TableName, req.TableID),
		})
	})
}
