package graphql

// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.41

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net/mail"

	"github.com/programme-lv/backend/internal/database"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/exp/slog"
)

// Login is the resolver for the login field.
func (r *mutationResolver) Login(ctx context.Context, username string, password string) (*User, error) {
	requestLogger := r.Logger.With(slog.String("request_type", "login"),
		slog.String("username", username))
	requestLogger.Info("received login request")

	user, err := database.SelectUserByUsername(r.PostgresDB, username)
	if err == sql.ErrNoRows {
		requestLogger.Info("user not found")
		return nil, fmt.Errorf("username or password is incorrect")
	} else if err != nil {
		requestLogger.Error("failed to get user from database", slog.String("error", err.Error()))
		return nil, err
	}

	requestLogger = requestLogger.With(slog.Int64("user_id", user.ID))

	// Verify the password
	err = bcrypt.CompareHashAndPassword([]byte(user.HashedPassword), []byte(password))
	if err != nil {
		requestLogger.Info("password is incorrect")
		return nil, fmt.Errorf("username or password is incorrect")
	}

	// Set the user ID in the session
	r.SessionManager.Put(ctx, "user_id", user.ID)

	requestLogger.Info("login successful")

	return &User{
		ID:       fmt.Sprintf("%d", user.ID),
		Username: user.Username,
	}, nil
}

// Register is the resolver for the register field.
func (r *mutationResolver) Register(ctx context.Context, username string, password string, email string, firstName string, lastName string) (*User, error) {
	requestLogger := r.Logger.With(slog.String("request_type", "register"),
		slog.String("username", username), slog.String("email", email), slog.String("first_name", firstName), slog.String("last_name", lastName))
	requestLogger.Info("received register request")

	// validate registration data
	if username == "" || password == "" {
		return nil, fmt.Errorf("username and password are required")
	}
	if len(password) < 8 {
		return nil, fmt.Errorf("password must be at least %d characters", 8)
	}
	if len(password) > 32 {
		return nil, fmt.Errorf("password must be at most %d characters", 32)
	}
	if len(username) < 3 {
		return nil, fmt.Errorf("username must be at least %d characters", 3)
	}
	if len(username) > 15 {
		return nil, fmt.Errorf("username must be at most %d characters", 15)
	}

	usernameExists, err := database.DoesUserExistByUsername(r.PostgresDB, username)
	if err != nil {
		return nil, err
	}
	if usernameExists {
		return nil, fmt.Errorf("user with that username already exists")
	}

	emailExists, err := database.DoesUserExistByEmail(r.PostgresDB, email)
	if err != nil {
		return nil, err
	}
	if emailExists {
		return nil, fmt.Errorf("user with that email already exists")
	}

	// validate email
	_, err = mail.ParseAddress(email)
	if err != nil {
		return nil, err
	}

	hashedPassword, err := r.HashPassword(password)
	if err != nil {
		return nil, err
	}

	err = database.CreateUser(r.PostgresDB, username, hashedPassword, email, firstName, lastName)
	if err != nil {
		return nil, err
	}

	user, err := database.SelectUserByUsername(r.PostgresDB, username)
	if err != nil {
		return nil, err
	}

	return &User{
		ID:        fmt.Sprintf("%d", user.ID),
		Username:  user.Username,
		Email:     user.Email,
		FirstName: user.FirstName,
		LastName:  user.LastName,
		IsAdmin:   user.IsAdmin,
	}, nil
}

// Logout is the resolver for the logout field.
func (r *mutationResolver) Logout(ctx context.Context) (bool, error) {
	// Delete the user ID from the sessiong
	userId, ok := r.SessionManager.Pop(ctx, "user_id").(int64)

	if ok {
		log.Println("User", userId, "logged out")
		return true, nil
	} else {
		log.Println("User attempted to log out without being logged in")
		return false, fmt.Errorf("not logged in")
	}
}

// Whoami is the resolver for the whoami field.
func (r *queryResolver) Whoami(ctx context.Context) (*User, error) {
	// Get the user ID from the session
	userID, ok := r.SessionManager.Get(ctx, "user_id").(int64)
	if !ok {
		return nil, fmt.Errorf("not logged in")
	}

	// Get the user from the database
	var user database.User
	err := r.PostgresDB.Get(&user, "SELECT * FROM users WHERE id = $1", userID)
	if err == sql.ErrNoRows {
		return nil, fmt.Errorf("user not found")
	}

	return &User{
		ID:        fmt.Sprintf("%d", user.ID),
		Username:  user.Username,
		Email:     user.Email,
		FirstName: user.FirstName,
		LastName:  user.LastName,
		IsAdmin:   user.IsAdmin,
	}, nil
}
