package middleware

import (
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/account"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/validators"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/user"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"

	"github.com/gin-gonic/gin"
)

type Validator struct {
	accountService *account.Service
	userService    *user.Service
	*handler.Context
}

func NewValidator(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Validator {
	return &Validator{accountService: account.NewService(account.NewDatabase(db)), userService: user.NewService(user.NewDatabase(db)), Context: ctx}
}
func (v Validator) HasValidUserId(gc *gin.Context) {
	userId := gc.Param("userId")
	err := v.Validator.VarCtx(gc, userId, "uuid4")
	if err != nil {
		utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
		return
	}
}

func (v Validator) UserExists(gc *gin.Context) {
	userId := gc.Param("userId")
	user, err := v.userService.GetUserById(userId)
	if e, ok := err.(*model.APIError); ok {
		utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
		utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
	} else if err != nil {
		utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
		utils.JSONError(gc.Writer, model.ErrInternalServer)
		return
	}

	if user == nil {
		utils.JSONErrorWithMessage(gc.Writer, model.ErrNotFound, "User not found")
		return
	}
}

func (v Validator) UserdIdMatchOAuthToken(gc *gin.Context) {
	userId := gc.Param("userId")
	usrParam, err := v.userService.GetUserById(userId)
	if e, ok := err.(*model.APIError); ok {
		utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
		utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
	} else if err != nil {
		utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
		utils.JSONError(gc.Writer, model.ErrInternalServer)
		return
	}
	googleUserId, exists := gc.Get("googleUserId")
	if !exists {
		utils.GetLoggerFromCtx(gc).Error("error while getting google user id")
		utils.JSONError(gc.Writer, model.ErrInternalServer)
		return
	}
	usr, err := v.userService.GetUserFromGoogleID(googleUserId.(string))
	if e, ok := err.(*model.APIError); ok {
		utils.GetLogger().Info(err)
		utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
		utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
		return
	} else if err != nil {
		utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
		utils.JSONError(gc.Writer, model.ErrInternalServer)
		return
	}

	if usr == nil || usr.ID != usrParam.ID {
		utils.GetLoggerFromCtx(gc).WithError(err).Error("User in path doesn't match authenticated user")
		utils.JSONError(gc.Writer, model.ErrBadRequestFormat)
		return
	}
}

func (v Validator) HasValidAccountId(gc *gin.Context) {
	accountId := gc.Param("accountId")
	err := v.Validator.VarCtx(gc, accountId, "uuid4")
	if err != nil {
		utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
		return
	}
}

func (v Validator) AccountExists(gc *gin.Context) {
	userId := gc.Param("userId")
	accountId := gc.Param("accountId")
	_, err := v.accountService.GetASpecificAccountForUser(userId, accountId)
	if e, ok := err.(*model.APIError); ok {
		utils.GetLogger().Info(err)
		utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
		utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
		return
	} else if err != nil {
		utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
		utils.JSONError(gc.Writer, model.ErrInternalServer)
		return
	}
}
