package common

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"time"

	"party.at/party/internal/data"
)

func (app *Application) LoginUser(email, password string) (*data.Token, error) {
	app.Logger.PrintInfo("login attempt", map[string]string{"email": email})

	user, err := app.Models.Users.GetByEmail(email)
	if err != nil {
		if errors.Is(err, data.ErrRecordNotFound) {
			app.Logger.PrintInfo("login failed: user not found", map[string]string{"email": email})
			return nil, data.ErrInvalidCredentials
		}
		return nil, err
	}

	identities, err := app.Models.UserIdentities.GetByUser(user.ID)
	if err != nil {
		if errors.Is(err, data.ErrRecordNotFound) {
			app.Logger.PrintInfo("login failed: no identities", map[string]string{"email": email, "user_id": fmt.Sprint(user.ID)})
			return nil, data.ErrInvalidCredentials
		}
		return nil, err
	}

	app.Logger.PrintInfo("login: found identities", map[string]string{"email": email, "count": fmt.Sprint(len(identities))})

	var authenticatedIdentity *data.UserIdentity
	for _, identity := range identities {
		match, err := identity.Password.Matches(password)
		if err != nil {
			return nil, err
		}
		if match {
			authenticatedIdentity = identity
			break
		}
	}

	if authenticatedIdentity == nil {
		app.Logger.PrintInfo("login failed: wrong password", map[string]string{"email": email})
		return nil, data.ErrInvalidCredentials
	}

	app.Logger.PrintInfo("login success", map[string]string{"email": email, "user_id": fmt.Sprint(user.ID)})
	return app.Models.Tokens.New(user.ID, authenticatedIdentity.ID, 24*time.Hour, data.ScopeAuthentication)
}

func (app *Application) GetUserFromToken(plaintext string) (*data.User, error) {
	userIdentity, err := app.Models.UserIdentities.GetForToken(data.ScopeAuthentication, plaintext)
	if err != nil {
		return nil, err
	}
	return app.Models.Users.Get(userIdentity.UserID)
}

func (app *Application) DeleteToken(plaintext string) error {
	hash := sha256.Sum256([]byte(plaintext))
	return app.Models.Tokens.Delete(hash[:])
}
