package user

import (
	"net/http"
	"time"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgtype"
	"github.com/shroff/phylum/server/internal/core/db"
	"github.com/shroff/phylum/server/internal/core/errors"
	"github.com/shroff/phylum/server/internal/core/util/rand"
)

var ErrTokenNotFound = errors.NewError(http.StatusBadRequest, "token_not_found", "Token Not Found")

const resetTokenLength = 64
const resetTokenDuration = 10 * time.Minute

func (m manager) CreateResetToken(user User) (string, error) {
	const q = `INSERT INTO reset_tokens(user_id, token, expires)
VALUES (@user_id::INT, @token::TEXT, @expires::TIMESTAMP)
ON CONFLICT(user_id) DO UPDATE SET token = @token::TEXT, expires = @expires::TIMESTAMP`
	token := rand.GenerateRandomString(resetTokenLength)

	args := pgx.NamedArgs{
		"user_id": user.ID,
		"token":   token,
		"expires": time.Now().Add(resetTokenDuration),
	}
	if _, err := m.db.Exec(q, args); err != nil {
		return "", err
	}
	return token, nil
}

func (m manager) ResetUserPassword(user User, token, password string) error {
	const q = `DELETE FROM reset_tokens WHERE user_id = @user_id::INT AND token = @token::TEXT RETURNING expires`
	args := pgx.NamedArgs{
		"user_id": user.ID,
		"token":   token,
		"expires": time.Now().Add(resetTokenDuration),
	}
	return m.db.RunInTx(func(d db.Handler) error {
		m := m.withDb(d)

		// UpdateUserPassword will ensure the password strength
		// Not incorrect to do this before token verification because we are in a transaction.
		// TODO: Are there perf implications for this in case of malicious actors?
		err := m.UpdateUserPassword(user, password)
		if err != nil {
			return err
		}
		row := m.db.QueryRow(q, args)
		var expires pgtype.Timestamp
		if err := row.Scan(&expires); err == nil {
			if time.Now().After(expires.Time) {
				return ErrTokenNotFound
			}
			return nil
		} else if errors.Is(err, pgx.ErrNoRows) {
			return ErrTokenNotFound
		} else {
			return err
		}
	})

}
