package server

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	domain "github.com/haydenhargreaves/Potion/internal/domain/recipe"
	domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
)

// GetRecipeOfTheWeekHandler fetchs the current recipe of the week and returns it.
// If an error occurs, it will be returned and a recipe will not be returned.
//
// BUG: Until auth is reimplemented, there is no way to determine what user is making the
// call.
// NOTE: I believe this issue has been resolved
func (s *Server) GetRecipeOfTheWeekHandlerV2(ctx *gin.Context) {
	userId := getUserId(ctx)
	recipe, err := s.deps.RecipeService.GetRecipeOfTheWeek(userId)

	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to get recipe of the week. %s", err.Error()),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  http.StatusOK,
		"message": "[OK] Successfully retrieved recipe of the week.",
		"recipe":  recipe,
	})
}

func (s *Server) GetRecipeHandlerV2(ctx *gin.Context) {
	id := ctx.Param("id")
	parsedId, err := strconv.Atoi(id)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()),
		})
		return
	}

	userId := getUserId(ctx)
	recipe, err := s.deps.RecipeService.GetRecipe(parsedId, userId)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to get recipe. %s", err.Error()),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  http.StatusOK,
		"message": "[OK] Successfully retrieved recipe.",
		"recipe":  recipe,
	})
}

func (s *Server) SearchRecipeHandlerV2(ctx *gin.Context) {
	var filters domain.SearchFilters

	// Parse filters
	if err := ctx.ShouldBindJSON(&filters); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to parse filters. %s", err.Error()),
		})
		return
	}

	// This is optional, so we can do this
	userId := getUserId(ctx)

	// Did I really have two APIs...?
	// TODO: Fix service at some point, no need to accept the favorites (bool) param
	recipes, err := s.deps.RecipeService.SearchRecipes(filters, userId, filters.Favorites)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to get searched recipes. %s", err.Error()),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  http.StatusOK,
		"message": "[OK] Successfully retrieved recipes based on provided filters.",
		"recipes": recipes,
	})
}

func (s *Server) CreateRecipeHandlerV2(ctx *gin.Context) {
	userId := getUserId(ctx)
	if userId == nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": "[ERROR] User must be logged in to create a recipe.",
		})
		return
	}

	recipe, err := s.deps.RecipeService.CreateRecipe(ctx)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to create recipe. %s", err.Error()),
		})
		return
	}

	_, err = s.deps.EngagementService.UserCreateRecipe(*userId, recipe.Id)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to create recipe engagement. %s", err.Error()),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  http.StatusOK,
		"message": "[OK] Successfully created new recipe.",
		"recipe":  recipe,
	})
}

func (s *Server) EditRecipeHandlerV2(ctx *gin.Context) {
	s.withAuthenticatedUser(ctx, func(ctx *gin.Context, user *domainUser.User) {
		id := ctx.Param("id")
		parsedId, err := strconv.Atoi(id)
		if err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{
				"status":  http.StatusBadRequest,
				"message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()),
			})
			return
		}

		recipe, err := s.deps.RecipeService.EditRecipe(ctx, parsedId, user.Id)

		_, err = s.deps.EngagementService.UserEditRecipe(user.Id, recipe.Id)
		if err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{
				"status":  http.StatusBadRequest,
				"message": fmt.Sprintf("[ERROR] Failed to create recipe engagement. %s", err.Error()),
			})
		return
	}

		ctx.JSON(http.StatusOK, gin.H{
			"status":  http.StatusOK,
			"message": "[OK] Successfully updated recipe.",
			"recipe":  recipe,
		})
	})
}

func (s *Server) DeleteRecipeHandlerV2(ctx *gin.Context) {
	s.withAuthenticatedUser(ctx, func(ctx *gin.Context, user *domainUser.User) {
		id := ctx.Param("id")
		parsedId, err := strconv.Atoi(id)
		if err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{
				"status":  http.StatusBadRequest,
				"message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()),
			})
			return
		}

		_, err = s.deps.EngagementService.UserDeleteRecipe(user.Id, parsedId)
		if err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{
				"status":  http.StatusBadRequest,
				"message": fmt.Sprintf("[ERROR] Failed to create recipe engagement. %s", err.Error()),
			})
			return
		}

		if err := s.deps.RecipeService.DeleteRecipe(user.Id, parsedId); err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{
				"status":  http.StatusBadRequest,
				"message": fmt.Sprintf("[ERROR] Failed to delete recipe. %s", err.Error()),
			})
			return
		}

		ctx.JSON(http.StatusOK, gin.H{
			"status":  http.StatusOK,
			"message": "[OK] Successfully deleted recipe.",
		})
	})
}

func (s *Server) IsRecipeOwnerV2(ctx *gin.Context) {
	userId := getUserId(ctx)

	id := ctx.Param("id")
	parsedId, err := strconv.Atoi(id)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"owner":   false,
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()),
		})
		return
	}

	isOwner, err := s.deps.RecipeService.IsRecipeOwner(userId, parsedId)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"owner":   false,
			"status":  http.StatusBadRequest,
			"message": fmt.Sprintf("[ERROR] Failed to determine is user is recipe owner.", err.Error()),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"owner":   isOwner,
		"status":  http.StatusOK,
		"message": "[OK] Successfully determined recipe ownership status.",
	})
}
