package service

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

	"github.com/calendarapi/internal/middleware"
	"github.com/calendarapi/internal/models"
	"github.com/calendarapi/internal/repository"
	"github.com/calendarapi/internal/utils"
	"github.com/google/uuid"
)

type APIKeyService struct {
	queries *repository.Queries
}

func NewAPIKeyService(queries *repository.Queries) *APIKeyService {
	return &APIKeyService{queries: queries}
}

func (s *APIKeyService) Create(ctx context.Context, userID uuid.UUID, name string, scopes map[string][]string) (*models.APIKeyResponse, error) {
	if name == "" {
		return nil, models.NewValidationError("name is required")
	}

	rawToken := make([]byte, 32)
	if _, err := rand.Read(rawToken); err != nil {
		return nil, models.ErrInternal
	}
	token := hex.EncodeToString(rawToken)
	hash := middleware.SHA256Hash(token)

	scopesJSON, err := json.Marshal(scopes)
	if err != nil {
		return nil, models.ErrInternal
	}

	keyID := uuid.New()
	key, err := s.queries.CreateAPIKey(ctx, repository.CreateAPIKeyParams{
		ID:      utils.ToPgUUID(keyID),
		UserID:  utils.ToPgUUID(userID),
		Name:    name,
		KeyHash: hash,
		Scopes:  scopesJSON,
	})
	if err != nil {
		return nil, models.ErrInternal
	}

	return &models.APIKeyResponse{
		ID:        utils.FromPgUUID(key.ID),
		Name:      key.Name,
		CreatedAt: utils.FromPgTimestamptz(key.CreatedAt),
		Token:     token,
	}, nil
}

func (s *APIKeyService) List(ctx context.Context, userID uuid.UUID) ([]models.APIKeyResponse, error) {
	keys, err := s.queries.ListAPIKeysByUser(ctx, utils.ToPgUUID(userID))
	if err != nil {
		return nil, models.ErrInternal
	}

	result := make([]models.APIKeyResponse, len(keys))
	for i, k := range keys {
		result[i] = models.APIKeyResponse{
			ID:        utils.FromPgUUID(k.ID),
			Name:      k.Name,
			CreatedAt: utils.FromPgTimestamptz(k.CreatedAt),
			RevokedAt: utils.FromPgTimestamptzPtr(k.RevokedAt),
		}
	}
	return result, nil
}

func (s *APIKeyService) Revoke(ctx context.Context, userID uuid.UUID, keyID uuid.UUID) error {
	return s.queries.RevokeAPIKey(ctx, repository.RevokeAPIKeyParams{
		ID:     utils.ToPgUUID(keyID),
		UserID: utils.ToPgUUID(userID),
	})
}
