package user

import (
	"fmt"
	"net/http"
	"nos-comptes/handler"
	"nos-comptes/internal/storage/dao/postgresql"
	"nos-comptes/internal/storage/model"
	"nos-comptes/internal/storage/validators"
	"nos-comptes/internal/utils"
	"strings"

	"github.com/gin-gonic/gin"
	"google.golang.org/api/oauth2/v2"
)

type Context struct {
	service *Service
	db      *Database
	*handler.Context
}

func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context {
	database := NewDatabase(db)
	service := NewService(database)
	return &Context{service: service, db: database, Context: ctx}
}

func (uc *Context) GetAllUsers(c *gin.Context) {
	users, err := uc.db.GetAllUsers()
	if err != nil {
		utils.GetLoggerFromCtx(c).WithError(err).Error("error while getting users")
		utils.JSONErrorWithMessage(c.Writer, model.ErrInternalServer, "Error while getting users")
		return
	}
	utils.JSON(c.Writer, http.StatusOK, users)
}

func (hc *Context) ConnectUser(c *gin.Context) {
	authorizationHeader := c.GetHeader("Authorization")
	authorizationHeaderSplitted := strings.Split(authorizationHeader, " ")
	if len(authorizationHeaderSplitted) != 2 {
		utils.JSONError(c.Writer, model.ErrBadRequestFormat)
		return
	}

	oauth2Service, err := oauth2.New(&http.Client{})
	if oauth2Service == nil {
		fmt.Println(err)
		utils.JSONError(c.Writer, model.ErrInternalServer)
		return
	}
	tokenInfoCall := oauth2Service.Tokeninfo()
	tokenInfoCall.IdToken(authorizationHeaderSplitted[1])
	tokenInfo, err := tokenInfoCall.Do()
	if err != nil {
		utils.GetLogger().WithError(err).Error(err)
		utils.JSONError(c.Writer, model.ErrBadRequestFormat)
		return
	}
	user, err := hc.service.GetUserFromGoogleID(tokenInfo.UserId)
	if err != nil {
		utils.GetLogger().WithError(err).Error(err)
		if castedError, ok := err.(*model.APIError); ok {
			if castedError.Type == model.ErrNotFound.Type {
				user, err := hc.service.CreateUserFromGoogleToken(tokenInfo.UserId, tokenInfo.Email)
				if err != nil {
					fmt.Println(err)
					utils.JSONError(c.Writer, model.ErrInternalServer)
					return
				}
				utils.JSON(c.Writer, 200, user)
				return
			}
			utils.JSONError(c.Writer, *castedError)
			return
		}
		utils.JSONError(c.Writer, model.ErrInternalServer)
		return
	}
	fmt.Println("Found the user " + user.Email)
	fmt.Println("Return 200")
	utils.JSON(c.Writer, 200, user)
}

func (hc *Context) CreateUser(c *gin.Context) {
	authorizationHeader := c.GetHeader("Authorization")
	authorizationHeaderSplitted := strings.Split(authorizationHeader, " ")
	if len(authorizationHeaderSplitted) != 2 {
		utils.JSONError(c.Writer, model.ErrBadRequestFormat)
		return
	}

	oauth2Service, err := oauth2.New(&http.Client{})
	if oauth2Service == nil {
		fmt.Println(err)
		utils.JSONError(c.Writer, model.ErrInternalServer)
		return
	}
	tokenInfoCall := oauth2Service.Tokeninfo()
	tokenInfoCall.IdToken(authorizationHeaderSplitted[1])
	tokenInfo, err := tokenInfoCall.Do()
	if err != nil {
		utils.GetLogger().WithError(err).Error(err)
		utils.JSONError(c.Writer, model.ErrBadRequestFormat)
		return
	}
	user, err := hc.service.GetUserFromGoogleID(tokenInfo.UserId)
	if err != nil {
		utils.GetLogger().WithError(err).Error(err)
		if castedError, ok := err.(*model.APIError); ok {
			if castedError.Type == model.ErrNotFound.Type {
				user, err := hc.service.CreateUserFromGoogleToken(tokenInfo.UserId, tokenInfo.Email)
				if err != nil {
					fmt.Println(err)
					utils.JSONError(c.Writer, model.ErrInternalServer)
					return
				}
				utils.JSON(c.Writer, http.StatusCreated, user)
				return
			}
			utils.JSONError(c.Writer, *castedError)
			return
		}
		utils.JSONError(c.Writer, model.ErrInternalServer)
		return
	}
	utils.JSON(c.Writer, http.StatusOK, user)
}

func (hc *Context) GetUser(c *gin.Context) {
	userID := c.Param("userId")

	err := hc.Validator.VarCtx(c, userID, "uuid4")
	if err != nil {
		utils.JSONError(c.Writer, validators.NewDataValidationAPIError(err))
		return
	}

	user, err := hc.service.GetUserById(userID)
	if e, ok := err.(*model.APIError); ok {
		utils.GetLoggerFromCtx(c).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
		utils.JSONErrorWithMessage(c.Writer, *e, e.Description)
	} else if err != nil {
		utils.GetLoggerFromCtx(c).WithError(err).Error("error while get user")
		utils.JSONError(c.Writer, model.ErrInternalServer)
		return
	}

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

	utils.JSON(c.Writer, http.StatusOK, user)
}
