package server

import (
	"fmt"
	"net/http"
	d "playback-device-server/data"
	m "playback-device-server/management"

	"github.com/labstack/echo/v4"
	"github.com/rs/zerolog/log"
)

type UsersApiHandler struct {
	router      *echo.Echo
	userManager *m.UserManager
}

func (r *UsersApiHandler) Initialize(authenticator *Authenticator) {
	r.router.Use(authenticator.Authenticate("/api/users", []string{"/api/users/login"}))
	usersApi := r.router.Group("/api/users")
	usersApi.POST("/login", r.handleLogin)
	usersApi.POST("/logout", r.handleLogout)
	usersApi.GET("/session/info", r.handleGetSessionInfo)
	usersApi.POST("/newpassword", r.handleChangePassword)
	usersApi.PUT("/:id", r.handleUpdateConfig)
	usersApi.GET("", r.handleGetUsers)
	usersApi.POST("", r.handleCreateUser)
	usersApi.DELETE("/:id", r.handleDeleteUser)
}

func (r UsersApiHandler) handleLogin(context echo.Context) error {
	var loginData struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}

	var errorResponse struct {
		Error string `json:"error"`
	}

	error := context.Bind(&loginData)

	if error != nil {
		log.Info().Msgf("failed to bind login data: %s", error)
		errorResponse.Error = fmt.Sprintf("%s", error)
		context.JSON(400, errorResponse)
		return error
	}

	token, error := r.userManager.Login(loginData.Username, loginData.Password)

	if error != nil {
		log.Info().Msgf("failed to login: %s", error)
		errorResponse.Error = fmt.Sprintf("%s", error)
		context.JSON(400, errorResponse)
		return error
	}

	thirdyDays := 30 * 24 * 60 * 60

	cookie := &http.Cookie{
		Name:     "token",
		Value:    token,
		Path:     "/",
		HttpOnly: true,
		MaxAge:   thirdyDays,
	}

	context.SetCookie(cookie)

	return context.JSON(200, "")
}

func (r UsersApiHandler) handleLogout(context echo.Context) error {
	r.removeTokenCookie(&context)
	context.JSON(200, "")

	authContext := context.(AuthContext)

	session := authContext.Session
	r.userManager.DeleteSession(session.Token)

	return nil
}

func (r UsersApiHandler) handleChangePassword(context echo.Context) error {
	authContext := context.(AuthContext)
	user := authContext.User

	var data struct {
		CurrentPassword string `json:"currentPassword"`
		NewPassword     string `json:"newPassword"`
	}

	error := context.Bind(&data)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to change password: %s", error))
		return error
	}

	error = r.userManager.UpdatePassword(data.CurrentPassword, data.NewPassword, user)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to change password: %s", error))
	}
	return error
}

func (r UsersApiHandler) handleGetSessionInfo(context echo.Context) error {
	authContext := context.(AuthContext)
	user := authContext.User

	response := struct {
		UserId   string `json:"user_id"`
		Username string `json:"username"`
		IsAdmin  bool   `json:"is_admin"`
	}{
		UserId:   user.ID,
		Username: user.Username,
		IsAdmin:  user.IsAdmin,
	}

	return context.JSON(200, response)
}

func (r UsersApiHandler) handleGetUsers(context echo.Context) error {
	users, error := r.userManager.GetUsers()
	if error != nil {
		SendError(500, context, fmt.Sprintf("failed to get user list: %s", error))
	}

	return context.JSON(200, users)
}

func (r UsersApiHandler) handleCreateUser(context echo.Context) error {
	authContext := context.(AuthContext)
	user := authContext.User

	if !user.IsAdmin {
		SendError(403, context, "permission denied")
		log.Info().Msg("failed to delete user: permission denied")
		return fmt.Errorf("permission denied")
	}

	var newUser d.User
	error := context.Bind(&newUser)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to create user: %s", error))
		return error
	}

	id, error := r.userManager.CreateUser(&newUser)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to create user: %s", error))
		return error
	}

	user.ID = id

	return context.JSON(200, user)
}

func (r UsersApiHandler) handleDeleteUser(context echo.Context) error {
	authContext := context.(AuthContext)
	user := authContext.User

	if !user.IsAdmin {
		SendError(403, context, "permission denied")
		log.Info().Msg("failed to delete user: permission denied")
		return fmt.Errorf("permission denied")
	}

	id := context.Param("id")

	exists, error := r.userManager.UserIdExists(id)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to delete user: %s", error))
		return error
	}

	if !exists {
		SendError(404, context, fmt.Sprintf("Failed to delete user: Could not find user id %s", id))
		return fmt.Errorf("not found")
	}

	error = r.userManager.DeleteUser(id)
	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to delete user: %s", error))
		return error
	}

	return context.JSON(200, "")
}

func (r *UsersApiHandler) removeTokenCookie(context *echo.Context) {
	cookie := &http.Cookie{
		Name:     "token",
		Path:     "/",
		HttpOnly: true,
		MaxAge:   -1,
	}

	(*context).SetCookie(cookie)
}

func (r *UsersApiHandler) handleUpdateConfig(context echo.Context) error {
	authContext := context.(AuthContext)
	user := authContext.User

	if !user.IsAdmin {
		SendError(403, context, "permission denied")
		log.Info().Msg("failed to delete user: permission denied")
		return fmt.Errorf("permission denied")
	}

	id := context.Param("id")

	data := struct {
		IsAdmin int `json:"is_admin"`
	}{
		IsAdmin: -1,
	}

	error := context.Bind(&data)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to update user config: %s", error))
		return error
	}

	updatingUser, error := r.userManager.GetUserById(id)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to update user config: %s", error))
		return error
	}

	if user.ID != id && data.IsAdmin != -1 {
		updatingUser.IsAdmin = data.IsAdmin == 1
	}

	error = r.userManager.UpdateUser(updatingUser)

	if error != nil {
		SendError(500, context, fmt.Sprintf("Failed to update user config: %s", error))
		return error
	}

	return context.JSON(200, updatingUser)
}

func (r *UsersApiHandler) SetRouter(router *echo.Echo) {
	r.router = router
}

func (r *UsersApiHandler) SetUserManager(userManager *m.UserManager) {
	r.userManager = userManager
}
