package controllers

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"git.dynamicdiscord.de/kalipso/zineshop/models"
	"git.dynamicdiscord.de/kalipso/zineshop/repositories"
	"git.dynamicdiscord.de/kalipso/zineshop/services"
)

type UserController struct{}

func NewUserController() UserController {
	return UserController{}
}

func (uc *UserController) Register(c *gin.Context) {
	//Get the email/passwd off req body
	var body struct {
		Name     string
		Email    string
		Password string
	}

	err := c.Bind(&body)

	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Failed to read body",
		})

		return
	}

	_, err = services.Users.Register(body.Name, body.Email, body.Password, false)

	if err != nil {
		fmt.Println("Error: ", err)
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Failed to create user",
		})

		return
	}

	//respond
	c.JSON(http.StatusOK, gin.H{})
}

func (uc *UserController) Login(c *gin.Context) {
	//Get the email/passwd off req body
	var body struct {
		Email    string
		Password string
	}

	err := c.Bind(&body)

	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Login Failed",
		})

		return
	}

	tokenString, err := services.Users.Login(body.Email, body.Password)

	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Login Failed",
		})

		return
	}

	// send it back
	c.SetSameSite(http.SameSiteLaxMode)
	c.SetCookie("Authorization", tokenString, 3600*24, "", "", false, true)
	c.JSON(http.StatusOK, gin.H{})
}

func (uc *UserController) Validate(c *gin.Context) {
	user, exists := c.Get("user")

	if exists {
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("Logged in with userID: %d", user.(models.User).ID),
		})
	} else {
		c.JSON(http.StatusOK, gin.H{
			"message": "Currently not logged in.",
		})
	}
}

func (rc *UserController) LoginView(c *gin.Context) {
	//if already logged in
	_, exists := c.Get("user")
	if exists {
		c.HTML(http.StatusOK, "index.html", CreateSessionData(c, gin.H{}))
		return
	}

	data := gin.H{
		"error": "",
	}

	c.HTML(http.StatusOK, "login.html", CreateSessionData(c, data))
}

func (rc *UserController) LoginHandler(c *gin.Context) {
	email := c.PostForm("email")
	password := c.PostForm("password")

	tokenString, err := services.Users.Login(email, password)

	if err != nil {
		data := gin.H{
			"error": "Login Failed. Wrong Email or Password!",
		}

		c.HTML(http.StatusOK, "login.html", data)
		return
	}

	//set this so that CreateSessionData works
	//otherwise header would not be generated correctly
	user, _ := repositories.Users.GetByEmail(email)
	c.Set("user", user)

	// send it back
	//c.SetSameSite(http.SameSiteLaxMode)
	c.SetCookie("Authorization", tokenString, 3600*24, "", "", false, true)
	c.HTML(http.StatusOK, "login.html", CreateSessionData(c, gin.H{}))
}

func CreateSessionData(c *gin.Context, extra any) gin.H {
	user, exists := c.Get("user")
	userImpl, _ := user.(models.User)

	return gin.H{
		"loggedIn": exists,
		"isAdmin":  userImpl.IsAdmin,
		"data":     extra,
	}
}

func (rc *UserController) RegisterHandler(c *gin.Context) {
	name := c.PostForm("name")
	email := c.PostForm("email")
	password := c.PostForm("password")

	//first registered user is admin
	isEmpty, _ := repositories.Users.IsEmpty()
	if isEmpty {
		_, err := services.Users.Register(name, email, password, true)
		if err != nil {
			data := gin.H{
				"error":   "Registering Failed.",
				"success": "",
			}
			c.HTML(http.StatusOK, "register.html", data)
			return
		}

		data := gin.H{
			"error":   "",
			"success": "You successfully registered as Admin. Try logging in.",
		}

		c.HTML(http.StatusOK, "register.html", data)
		return
	}

	//for any other user token is required
	token := c.PostForm("token")

	if token == "" {
		data := gin.H{
			"error":   "No token. No register.",
			"success": "",
		}

		c.HTML(http.StatusOK, "register.html", data)
		return
	}

	tokenExists, err := repositories.Tokens.Exists(token)

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		data := gin.H{
			"error":   err,
			"success": "",
		}

		c.HTML(http.StatusOK, "register.html", data)
		return
	}

	if !tokenExists {
		data := gin.H{
			"error":   "Invalid Token.",
			"success": "",
		}
		c.HTML(http.StatusOK, "register.html", data)
		return
	}

	_, err = services.Users.Register(name, email, password, false)
	if err != nil {
		data := gin.H{
			"error":   "Registering Failed.",
			"success": "",
		}
		c.HTML(http.StatusOK, "register.html", data)
		return
	}

	err = repositories.Tokens.Delete(token)

	if err != nil {
		fmt.Println("Could not delete RegisterToken: ", err)
	}

	data := gin.H{
		"error":   "",
		"success": "You successfully registered. Try logging in.",
	}

	c.HTML(http.StatusOK, "register.html", data)
}

func (rc *UserController) RegisterView(c *gin.Context) {
	data := gin.H{
		"error":   "",
		"success": "",
		"token":   c.Param("token"),
	}

	c.HTML(http.StatusOK, "registertoken.html", data)
}

func (rc *UserController) InitAdmin(c *gin.Context) {
	isEmpty, err := repositories.Users.IsEmpty()

	if err != nil {
		data := gin.H{
			"error":   err,
			"success": "",
		}

		c.HTML(http.StatusInternalServerError, "error.html", data)
		return
	}

	if !isEmpty {
		data := gin.H{
			"error":   "Registration is closed",
			"success": "",
		}

		c.HTML(http.StatusInternalServerError, "error.html", data)
		return

	}

	data := gin.H{
		"error":   "",
		"success": "",
	}

	c.HTML(http.StatusOK, "register.html", data)
}

func (rc *UserController) ResetView(c *gin.Context) {
	shopItems, _ := repositories.ShopItems.GetAll()

	data := gin.H{
		"title":     "shopItem Page",
		"shopItems": shopItems,
	}

	c.HTML(http.StatusOK, "passwordreset.html", data)
}

func (rc *UserController) ResetHandler(c *gin.Context) {
	shopItems, _ := repositories.ShopItems.GetAll()

	data := gin.H{
		"title":     "shopItem Page",
		"shopItems": shopItems,
	}

	c.HTML(http.StatusOK, "passwordreset.html", data)
}

func (rc *UserController) InviteView(c *gin.Context) {
	tokens, _ := repositories.Tokens.GetAll()
	fmt.Println(tokens)

	data := gin.H{
		"tokens": tokens,
	}

	c.HTML(http.StatusOK, "invites.html", data)
}

func (rc *UserController) InviteHandler(c *gin.Context) {
	action := c.PostForm("action")

	if action == "create" {
		_, err := repositories.Tokens.Create()

		if err != nil {
			fmt.Println(err)
			c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err})
			return
		}
	}

	if action == "delete" {
		token := c.PostForm("token")
		repositories.Tokens.Delete(token)
	}

	rc.InviteView(c)
}

func (rc *UserController) MainView(c *gin.Context) {
	itemOrder := c.Query("order")

	var shopItems []models.ShopItem
	if itemOrder == "newestFirst" {
		shopItems, _ = repositories.ShopItems.GetAllNewestFirst()
	} else if itemOrder == "oldestFirst" {
		shopItems, _ = repositories.ShopItems.GetAllNewestLast()
	} else if itemOrder == "az" {
		shopItems, _ = repositories.ShopItems.GetAllLexicalFirst()
	} else if itemOrder == "za" {
		shopItems, _ = repositories.ShopItems.GetAllLexicalLast()
	} else {
		shopItems, _ = repositories.ShopItems.GetAllNewestFirst()
	}

	data := CreateSessionData(c, gin.H{
		"title":     "shopItem Page",
		"shopItems": shopItems,
	})

	c.HTML(http.StatusOK, "index.html", data)
}

func (rc *UserController) TagView(c *gin.Context) {
	shopItems, _ := repositories.ShopItems.GetByTagId(c.Param("id"))

	data := CreateSessionData(c, gin.H{
		"title":     "shopItem Page",
		"shopItems": shopItems,
	})

	c.HTML(http.StatusOK, "index.html", data)
}

func (rc *UserController) Logout(c *gin.Context) {
	c.SetCookie("Authorization", "", -1, "", "", false, true)
	c.HTML(http.StatusOK, "index.html", gin.H{})
}
