package accountdb

import (
	"context"

	"github.com/pingcap/errcode"
	"github.com/pingcap/log"
	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/account/idl/pbgen/proto/acl"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/account/accounttypes"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/grpc/grpcli"
	"github.com/tidbcloud/pkgv2/bizerr"
	"go.uber.org/zap"
)

type User struct {
	accounttypes.User
	Role           acl.OrgRole
	TenantID       uint64
	IsImpersonated bool
}

func GetUserByID(userID uint64) (*accounttypes.User, error) {
	ctx := context.Background()

	response, err := grpcli.GetUserByID(ctx, userID)
	if err != nil {
		return nil, err
	}

	return ConvertUser(response), nil
}

func GetUserByEmail(email string) (*accounttypes.User, error) {
	ctx := context.Background()

	response, err := grpcli.GetUserByEmail(ctx, email)
	if err != nil {
		return nil, err
	}

	return ConvertUser(response), nil
}

func GetUserInOrg(email string, orgID uint64, skipCheckOrg bool) (*User, bool, error) {
	getAuthUserRequest := &account.GetAuthUserRequest{
		OrgId:        orgID,
		UserEmail:    email,
		SkipCheckOrg: skipCheckOrg,
	}
	authUser, err := grpcli.GetAuthUser(context.Background(), getAuthUserRequest)
	if err != nil {
		if err.RawErrCode() == bizerr.ErrCodeRecordNotFound {
			return nil, false, nil
		}
		if err.RawErrCode() == bizerr.ErrCodePermission {
			return nil, false, errcode.NewForbiddenErr(err)
		}
		log.L().Warn("Get auth user failed", zap.String("Email", email), zap.Uint64("OrgID", orgID), zap.Error(err))
		return nil, false, errcode.NewNotAuthenticatedErr(err)
	}
	result := ConvertAuthUserToUser(authUser)

	return result, true, nil
}

func ConvertAuthUserToUser(authUser *account.GetAuthUserResponse) *User {
	user := ConvertUser(authUser.User)
	org := convertOrg(authUser.Org)
	user.Tenant = org
	user.TenantID = org.ID
	for _, project := range authUser.AvailableProjects {
		user.Projects = append(user.Projects, *convertProject(project))
	}

	result := &User{
		User:           *user,
		Role:           authUser.OrgRole,
		TenantID:       org.ID,
		IsImpersonated: authUser.IsImpersonated,
	}
	return result
}

func GetOrCreateSystemUser(orgID uint64) (*accounttypes.User, error) {
	ctx := context.Background()
	request := &account.GetOrCreateSystemUserRequest{
		OrgId: orgID,
	}

	response, err := grpcli.GetOrCreateSystemUser(ctx, request)
	if err != nil {
		return nil, err
	}

	return ConvertUser(response.User), nil
}
