package auth

import (
	"context"
	"errors"
	"fmt"

	"golang.org/x/crypto/bcrypt"

	"a301_game_server/internal/db"
)

var (
	ErrInvalidCredentials = errors.New("invalid credentials")
	ErrUsernameTaken      = errors.New("username already taken")
)

// Service handles account registration and authentication.
type Service struct {
	pool *db.Pool
}

// NewService creates a new auth service.
func NewService(pool *db.Pool) *Service {
	return &Service{pool: pool}
}

// Register creates a new account. Returns the account ID.
func (s *Service) Register(ctx context.Context, username, password string) (int64, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return 0, fmt.Errorf("hash password: %w", err)
	}

	var id int64
	err = s.pool.QueryRow(ctx,
		`INSERT INTO accounts (username, password) VALUES ($1, $2)
		 ON CONFLICT (username) DO NOTHING
		 RETURNING id`,
		username, string(hash),
	).Scan(&id)

	if err != nil {
		return 0, ErrUsernameTaken
	}

	return id, nil
}

// Login validates credentials and returns the account ID.
func (s *Service) Login(ctx context.Context, username, password string) (int64, error) {
	var id int64
	var hash string

	err := s.pool.QueryRow(ctx,
		`SELECT id, password FROM accounts WHERE username = $1`, username,
	).Scan(&id, &hash)

	if err != nil {
		return 0, ErrInvalidCredentials
	}

	if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil {
		return 0, ErrInvalidCredentials
	}

	return id, nil
}
