package auth

import (
	"context"

	"github.com/gogo/protobuf/types"
	"github.com/pingcap/dbaas/central/service"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/idl/pbgen/proto/central"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
	"gopkg.in/auth0.v4/management"
)

type authService struct {
	central.UnimplementedAuthServiceServer
	service.OperationsService
	management *management.Management
}

var _ central.AuthServiceServer = (*authService)(nil)

// NewService constructs an AuthServiceServer
func NewService(s service.OperationsService, management *management.Management) central.AuthServiceServer {
	return &authService{OperationsService: s, management: management}
}

func (s *authService) Meta(ctx context.Context, _ *types.Empty) (*central.MetaData, error) {
	user := s.CurrentUser(ctx)
	email := user.Email
	users, err := s.management.User.ListByEmail(email)
	if err != nil || len(users) < 1 {
		log.Error(ctx, "failed to list users by email in Auth0", zap.String("email", email), zap.Error(err))
		return nil, errors.Errorf("failed to list users by email[%s] in Auth0", email)
	}
	auth0User := users[0]
	useMfa := false
	val, ok := auth0User.UserMetadata["use_mfa"]
	if ok && val.(bool) {
		useMfa = true
	}
	return &central.MetaData{
		Email:    email,
		TenantId: user.TenantID.Uint64(),
		UseMfa:   useMfa,
	}, nil
}

func (s *authService) UpdateMeta(ctx context.Context, req *central.UpdateMetaReq) (*types.Empty, error) {
	user := s.CurrentUser(ctx)
	email := user.Email
	users, err := s.management.User.ListByEmail(email)
	if err != nil || len(users) < 1 {
		log.Error(ctx, "failed to list users by email in Auth0", zap.String("email", email), zap.Error(err))
		return &types.Empty{}, errors.Errorf("failed to list users by email[%s] in Auth0", email)
	}
	for _, u := range users {
		updateUser := &management.User{
			UserMetadata: u.UserMetadata,
		}
		if updateUser.UserMetadata == nil {
			updateUser.UserMetadata = make(map[string]interface{})
		}
		updateUser.UserMetadata["use_mfa"] = req.UseMfa
		err := s.management.User.Update(*u.ID, updateUser)
		if err != nil {
			log.Error(ctx, "failed to update user metadata in Auth0", zap.String("email", email), zap.Error(err))
			return &types.Empty{}, errors.Errorf("failed to update user metadata of %s in Auth0", email)
		}
	}
	return &types.Empty{}, nil
}
