package auth

import (
	"context"
	"crypto/rand"
	"encoding/hex"

	"codeberg.org/nextgo/dbots/internal/db"
	"codeberg.org/nextgo/dbots/internal/discord"
)

type Service struct {
	q      *db.Queries
	client *discord.Client
}

func NewService(q *db.Queries, client *discord.Client) *Service {
	return &Service{q: q, client: client}
}

// Callback exchanges the OAuth code for a Discord access token,
// fetches the Discord user, and upserts them in the database.
// It returns the db.User and the raw Discord access token
// (needed so the caller can store it in the session if desired).
func (s *Service) Callback(ctx context.Context, code string) (*db.User, *discord.TokenResponse, error) {
	tok, err := s.client.ExchangeCode(ctx, code)
	if err != nil {
		return nil, nil, err
	}

	dUser, err := s.client.GetCurrentUser(ctx, tok.AccessToken)
	if err != nil {
		return nil, nil, err
	}

	user, err := s.q.GetUser(ctx, dUser.ID)
	if err != nil {
		// First login — create the user.
		user, err = s.q.CreateUser(ctx, db.CreateUserParams{
			ID:       dUser.ID,
			Username: dUser.Username,
		})
		if err != nil {
			return nil, nil, err
		}
	}

	return user, tok, nil
}

// GenerateState returns a cryptographically random hex string for
// the OAuth2 state parameter.
func GenerateState() (string, error) {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return "", err
	}
	return hex.EncodeToString(b), nil
}
