// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/user/publiclookup.go
package user

import (
	"context"
	"encoding/base64"
	"strings"

	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
	uc_user "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/user"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/validation"
)

type UserPublicLookupRequestDTO struct {
	Email string `json:"email"`
}

type UserPublicLookupResponseDTO struct {
	UserID            string `json:"user_id"`
	Email             string `json:"email"`
	Name              string `json:"name"`                 // Optional: for display
	PublicKeyInBase64 string `json:"public_key_in_base64"` // Base64 encoded
	VerificationID    string `json:"verification_id"`
}

type UserPublicLookupService interface {
	Execute(ctx context.Context, req *UserPublicLookupRequestDTO) (*UserPublicLookupResponseDTO, error)
}

type userPublicLookupServiceImpl struct {
	config           *config.Config
	logger           *zap.Logger
	userGetByEmailUC uc_user.UserGetByEmailUseCase
}

func NewUserPublicLookupService(
	cfg *config.Config,
	logger *zap.Logger,
	userGetByEmailUC uc_user.UserGetByEmailUseCase,
) UserPublicLookupService {
	logger = logger.Named("UserPublicLookupService")
	return &userPublicLookupServiceImpl{cfg, logger, userGetByEmailUC}
}

func (svc *userPublicLookupServiceImpl) Execute(ctx context.Context, req *UserPublicLookupRequestDTO) (*UserPublicLookupResponseDTO, error) {
	//
	// STEP 1: Sanitization of the input.
	//

	// Defensive Code: For security purposes we need to perform some sanitization on the inputs.
	req.Email = strings.ToLower(req.Email)
	req.Email = strings.ReplaceAll(req.Email, " ", "")
	req.Email = strings.ReplaceAll(req.Email, "\t", "")
	req.Email = strings.TrimSpace(req.Email)

	svc.logger.Debug("sanitized email",
		zap.String("email", validation.MaskEmail(req.Email)))

	//
	// STEP 2: Validation of input.
	//

	e := make(map[string]string)
	if req.Email == "" {
		e["email"] = "Email is required"
	}
	if len(req.Email) > 255 {
		e["email"] = "Email is too long"
	}

	if len(e) != 0 {
		svc.logger.Warn("failed validating",
			zap.Any("e", e))
		return nil, httperror.NewForBadRequest(&e)
	}

	//
	// STEP 3: Lookup user by email
	//

	// Lookup the user in our database, else return a `400 Bad Request` error.
	// Note: We return a generic error message to prevent user enumeration attacks.
	u, err := svc.userGetByEmailUC.Execute(ctx, req.Email)
	if err != nil {
		svc.logger.Error("failed getting user by email from database",
			zap.Any("error", err))
		return nil, httperror.NewForBadRequestWithSingleField("email", "Unable to complete lookup")
	}
	if u == nil {
		svc.logger.Warn("user lookup attempted for non-existent email",
			zap.String("email", validation.MaskEmail(req.Email)))
		// Return same error message as above to prevent user enumeration
		return nil, httperror.NewForBadRequestWithSingleField("email", "Unable to complete lookup")
	}

	// STEP 4: Build response DTO
	dto := &UserPublicLookupResponseDTO{
		UserID:            u.ID.String(),
		Email:             u.Email,
		Name:              u.Name,
		PublicKeyInBase64: base64.StdEncoding.EncodeToString(u.SecurityData.PublicKey.Key),
		VerificationID:    u.SecurityData.VerificationID,
	}

	return dto, nil
}
