package server

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

	"github.com/a-h/templ"
	"github.com/gin-gonic/gin"
	domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
	domain "github.com/haydenhargreaves/Potion/internal/domain/server"
	"github.com/haydenhargreaves/Potion/internal/templates/components"
	layouts "github.com/haydenhargreaves/Potion/internal/templates/layouts"
	pages "github.com/haydenhargreaves/Potion/internal/templates/pages"
)

func (s *Server) LoginPageHandler(ctx *gin.Context) {
	title := "Potion - Login"
	page := pages.LoginPage()

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) HomePageHandler(ctx *gin.Context) {
	loggedIn := domain.IsLoggedIn(ctx)

	// Ensure user is logged in with a valid account
	if loggedIn {
		if user := s.deps.UserService.GetAuthenicatedUser(ctx); user == nil {
			// Log (stale) user out
			s.SetCookie(ctx, "jwt_token", "", -1)
			s.SetCookie(ctx, "search-filters", "", -1)
			loggedIn = false
		}
	}

	var page templ.Component
	if loggedIn {
		userId := ctx.MustGet("userId").(int)
		madeRecipes, err := s.deps.RecipeService.GetUserMadeRecipes(userId, 6)
		if err != nil {
			components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting made recipes. %s\n", err.Error()))
			ctx.JSON(http.StatusInternalServerError, gin.H{
				"status":  http.StatusInternalServerError,
				"message": fmt.Sprintf("Error getting made recipes. %s\n", err.Error()),
			})
			return
		}
		viewedRecipes, err := s.deps.RecipeService.GetUserViewedRecipes(userId, 6)
		if err != nil {
			components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting viewed recipes. %s\n", err.Error()))
			ctx.JSON(http.StatusInternalServerError, gin.H{
				"status":  http.StatusInternalServerError,
				"message": fmt.Sprintf("Error getting viewed recipes. %s\n", err.Error()),
			})
			return
		}

		// Get the recipe of the week
		recipeOfTheWeek, err := s.deps.RecipeService.GetRecipeOfTheWeek(&userId)
		if err != nil {
			components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()))
			ctx.JSON(http.StatusInternalServerError, gin.H{
				"status":  http.StatusInternalServerError,
				"message": fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()),
			})
			return
		}

		if bytes, err := ctx.Cookie("search-filters"); err != nil {
			fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
			page = pages.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek, nil)
		} else {
			var filters domainRecipe.SearchFilters
			if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
				fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
				page = pages.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek, nil)
			} else {
				page = pages.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek, &filters)
			}
		}
	} else {
		// Get the recipe of the week
		recipeOfTheWeek, err := s.deps.RecipeService.GetRecipeOfTheWeek(nil)
		if err != nil {
			components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()))
			ctx.JSON(http.StatusInternalServerError, gin.H{
				"status":  http.StatusInternalServerError,
				"message": fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()),
			})
			return
		}

		if bytes, err := ctx.Cookie("search-filters"); err != nil {
			fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
			page = pages.HomePage(false, nil, nil, recipeOfTheWeek, nil)
		} else {
			var filters domainRecipe.SearchFilters
			if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
				fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
				page = pages.HomePage(false, nil, nil, recipeOfTheWeek, nil)
			} else {
				page = pages.HomePage(false, nil, nil, recipeOfTheWeek, &filters)
			}
		}
	}

	title := "Potion - Home"

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) FavoritesPageHandler(ctx *gin.Context) {
	// If not logged in, direct to the login page
	if !domain.IsLoggedIn(ctx) {
		ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
		return
	}

	title := "Potion - Favorites"
	var page templ.Component

	// Get filters from cookies
	if bytes, err := ctx.Cookie("search-filters"); err != nil {
		fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
		page = pages.FavoritesPage(nil)
	} else {
		var filters domainRecipe.SearchFilters
		if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
			fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
			page = pages.FavoritesPage(nil)
		} else {
			page = pages.FavoritesPage(&filters)
		}
	}

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) CreatePageHandler(ctx *gin.Context) {
	// If not logged in, direct to the login page
	if !domain.IsLoggedIn(ctx) {
		ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
		return
	}

	// Ensure user is logged in with a valid account
	if user := s.deps.UserService.GetAuthenicatedUser(ctx); user == nil {
		// Log (stale) user out
		s.SetCookie(ctx, "jwt_token", "", -1)
		s.SetCookie(ctx, "search-filters", "", -1)

		ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
		return
	}

	title := "Potion - Create"
	page := pages.CreatePage()

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) ProfilePageHandler(ctx *gin.Context) {
	// If not logged in, direct to the login page
	if !domain.IsLoggedIn(ctx) {
		ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
		return
	}

	// Else, get the user data
	user := s.deps.UserService.GetAuthenicatedUser(ctx)
	if user == nil {
		// User is failing to be found, direct to the login page
		ctx.Redirect(http.StatusSeeOther, domain.WEB_LOGIN)
		return
	}

	recipes, err := s.deps.RecipeService.GetUserRecipes(user.Id)
	if err != nil {
		components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting recipes. %s\n", err.Error()))
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  http.StatusInternalServerError,
			"message": fmt.Sprintf("Error getting recipes. %s\n", err.Error()),
		})
		return
	}

	favorites, err := s.deps.RecipeService.GetUserFavoriteRecipes(user.Id)
	if err != nil {
		components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting favorite recipes. %s\n", err.Error()))
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  http.StatusInternalServerError,
			"message": fmt.Sprintf("Error getting favorite recipes. %s\n", err.Error()),
		})
		return
	}

	// Get the engagement data, not sure what will happen when errors occur
	engagements, err := s.deps.EngagementService.GetUserEngagement(user.Id, 6)
	if err != nil {
		components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting user engagements. %s\n", err.Error()))
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  http.StatusInternalServerError,
			"message": fmt.Sprintf("Error getting user engagements. %s\n", err.Error()),
		})
		return
	}

	title := "Potion - Profile"
	page := pages.ProfilePage(*user, recipes, favorites, engagements)

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) ListPageHandler(ctx *gin.Context) {
	title := "Potion - Shopping List"
	page := pages.ListPage()

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) RecipePageHandler(ctx *gin.Context) {
	// Call recipe service to get via ID
	id := ctx.Param("id")

	// Parse ID
	parsed, err := strconv.Atoi(id)
	if err != nil {
		components.RenderErrorBanner(ctx, fmt.Sprintf("ERROR: %s", err.Error()))
		ctx.JSON(400, err.Error())
		return
	}

	// Get signed in user, if they exist
	var userId *int = nil
	var loggedIn = domain.IsLoggedIn(ctx)

	// Ensure user is logged in with a valid account
	if user := s.deps.UserService.GetAuthenicatedUser(ctx); user == nil {
		// Log (stale) user out
		s.SetCookie(ctx, "jwt_token", "", -1)
		s.SetCookie(ctx, "search-filters", "", -1)
		loggedIn = false
	}

	if loggedIn {
		storeId := ctx.MustGet("userId").(int)
		userId = &storeId
	}

	// Get recipe
	recipe, err := s.deps.RecipeService.GetRecipe(parsed, userId)
	if err != nil {
		components.RenderErrorBanner(ctx, fmt.Sprintf("ERROR: %s", err.Error()))
		ctx.JSON(400, err.Error())
		return
	}

	// Get user (owner)
	user, err := s.deps.UserService.GetUser(recipe.UserId)
	if err != nil {
		components.RenderErrorBanner(ctx, fmt.Sprintf("ERROR: %s", err.Error()))
		ctx.JSON(400, err.Error())
		return
	}

	title := "Potion - View Recipe"
	page := pages.RecipePage(*recipe, *user, loggedIn, s.deps.EnvironmentConfig.Domain)

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) SearchPageHandler(ctx *gin.Context) {
	var page templ.Component
	// Get filters from cookies
	if bytes, err := ctx.Cookie("search-filters"); err != nil {
		fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
		page = pages.SearchPage(nil, false)
	} else {
		var filters domainRecipe.SearchFilters
		if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
			fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
			page = pages.SearchPage(nil, false)
		} else {
			page = pages.SearchPage(&filters, true)
		}
	}

	title := "Potion - Recipe Search"

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

func (s *Server) NotFoundPageHandler(ctx *gin.Context) {
	title := "Potion - Not Found"
	page := pages.NotFoundPage()

	ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
