package controllers

import (
	"context"
	"log"
	"net/http"
	"ordr-api/dto"
	"ordr-api/queries"
	"ordr-api/utils"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

func SetUserName(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, err := pool.Acquire(ctx)
		defer conn.Release()
		if err != nil {
			// TODO: Log this error
			ctx.JSON(http.StatusInternalServerError, err.Error())
			log.Printf("SetUserName(): ERROR... Failed to acquire connection... %s", err.Error())
			return
		}
		user_profile, _ := ctx.Get("user_profile")

		user_name := ctx.Query("user_name")

		if user_name == "" {
			ctx.JSON(http.StatusBadRequest, "CreateUser(): ERROR: user name not supplied")
			return
		}

		_, update_err := conn.Exec(context.Background(), queries.USER_UPDATE_QUERY, user_name, user_profile.(dto.UserProfileResponse).Sub)
		if update_err != nil {
			log.Printf("%s", update_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
		}
	}
}

func PromoteUser(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		defer conn.Release()
		if conn_err != nil {
			ctx.JSON(http.StatusInternalServerError, conn_err.Error())
		}

		user_id := ctx.Query("user_id")
		if user_id == "" {
			ctx.JSON(http.StatusBadRequest, "PromoteUser(): ERROR Missing user id")
			return
		}

		_, update_err := conn.Exec(context.Background(), queries.USER_SET_IS_ADMIN_QUERY, user_id)
		if update_err != nil {
			ctx.JSON(http.StatusInternalServerError, update_err.Error())
		}
	}
}

func DemoteUser(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			ctx.JSON(http.StatusInternalServerError, conn_err.Error())
			return
		}
		defer conn.Release()
		user_id := ctx.Query("user_id")
		if user_id == "" {
			ctx.JSON(http.StatusBadRequest, "ERROR: User Id Not Supplied")
			return
		}
		_, exec_err := conn.Exec(context.Background(), queries.USER_REVOKE_ADMIN_QUERY, user_id)
		if exec_err != nil {
			ctx.JSON(http.StatusInternalServerError, exec_err.Error())
		}
	}
}

func DeactivateUser(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("DeactivateUser(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
		}
		defer conn.Release()

		user_id := ctx.Query("user_id")
		if user_id == "" {
			ctx.JSON(http.StatusBadRequest, "DeactivateUser(): User id not supplied")
			return
		}

		conn.Exec(context.Background(), queries.USER_SET_INACTIVE_QUERY, user_id)
	}
}

func ActivateUser(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("DeactivateUser(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
		}
		defer conn.Release()

		user_id := ctx.Query("user_id")
		if user_id == "" {
			ctx.JSON(http.StatusBadRequest, "DeactivateUser(): User id not supplied")
			return
		}

		conn.Exec(context.Background(), queries.USER_SET_ACTIVE_QUERY, user_id)
	}
}

func GetUserTable(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("GetUserTable(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
		}
		defer conn.Release()

		var request dto.UserRequest
		body_read_err := ctx.ShouldBindJSON(&request)
		if body_read_err != nil {
			ctx.JSON(http.StatusBadRequest, "GetUserTable(): ERROR... Invalid user query object")
			return
		}

		page := ctx.Query("page")
		if page == "" {
			ctx.JSON(http.StatusBadRequest, "GetUserTable(): Missing page")
			return
		}
		page_int, conv_err := strconv.Atoi(page)
		if conv_err != nil {
			ctx.JSON(http.StatusBadRequest, "GetUserTable(): Not an integer")
			return
		}
		rows, query_err := conn.Query(context.Background(), queries.USER_GET_TABLE_DATA, page_int*utils.PAGE_SIZE, utils.PAGE_SIZE, request.Name, request.JobPosition)
		if query_err != nil {
			ctx.JSON(http.StatusInternalServerError, "GetUserTable(): Failed to query database...")
			log.Printf("GetUserTable(): ERROR... %s", query_err.Error())
		}
		defer rows.Close()

		var users []dto.UserResponse
		for rows.Next() {
			var user dto.UserResponse
			scan_err := rows.Scan(&user.Id, &user.Name, &user.JobPosition, &user.Active, &user.Admin)
			if scan_err != nil {
				ctx.JSON(http.StatusInternalServerError, "GetUserTable(): ERROR: Failed to scan..")
				log.Printf("GetUserTable(): ERROR... %s", scan_err.Error())
				return
			}

			users = append(users, user)
		}

		ctx.JSON(http.StatusOK, users)
	}
}

func GetCurrentAuthenticatedUser(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("GetUserTable(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
		}
		defer conn.Release()

		user_profile, _ := ctx.Get("user_profile")

		log.Printf("%s", user_profile.(dto.UserProfileResponse).Sub)

		sub_id := user_profile.(dto.UserProfileResponse).Sub

		var user dto.UserResponse

		query_err := conn.QueryRow(context.Background(), queries.GET_CURRENT_USER_OBJECT, sub_id).Scan(&user.Id, &user.Name, &user.JobPosition, &user.Active, &user.Admin)
		if query_err != nil {
			ctx.JSON(http.StatusInternalServerError, "GetCurrentAuthenticatedUser(): ERROR.... Failed to query")
			log.Printf("GetCurrentAuthenticatedUser(): ERROR in querying user table... %s", query_err.Error())
			return
		}

		ctx.JSON(http.StatusOK, user)
	}
}

func CreatePosition(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("GetUserTable(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
			return
		}
		defer conn.Release()

		position_name := ctx.Query("position_name")
		if position_name == "" {
			ctx.JSON(http.StatusBadRequest, "CreatePosition(): ERROR... Position name not supplied!")
			return
		}

		_, exec_err := conn.Exec(context.Background(), queries.CREATE_POSITION, position_name)
		if exec_err != nil {
			ctx.JSON(http.StatusInternalServerError, "CreatePosition(): ERROR... exec failed")
			log.Printf("CreatePosition(): ERROR... exec failed... %s", exec_err.Error())
			return
		}
	}
}

func SetUserPosition(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("GetUserTable(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
		}
		defer conn.Release()

		position_name := ctx.Query("position")
		user_id := ctx.Query("user_id")

		if position_name == "" || user_id == "" {
			ctx.JSON(http.StatusBadRequest, "SetUserPosition(): ERROR... Missing required parameter")
		}

		var position_id string
		var position_name_query string
		query_err := conn.QueryRow(context.Background(), queries.POSITION_GET_POSITION, position_name).Scan(&position_id, &position_name_query)
		if query_err != nil {
			if query_err == pgx.ErrNoRows {
				ctx.JSON(http.StatusBadRequest, "SetUserPosition(): ERROR... No such position exists.")
				return
			}
			ctx.JSON(http.StatusInternalServerError, "SetUserPosition(): ERROR... Failed to query")
			log.Printf("SetUserPosition(): ERROR... Failed to query position table... %s", query_err.Error())
			return
		}

		_, exec_err := conn.Exec(context.Background(), queries.USER_SET_POSITION, position_id, user_id)
		if exec_err != nil {
			ctx.JSON(http.StatusInternalServerError, "SetUserPosition(): ERROR... failed to update user object")
			log.Printf("SetUserPosition(): ERROR... Failed to update user object... %s", exec_err.Error())
			return
		}
	}
}
