package grpc

// @Time    : 2022/11/14 4:47 PM
// @Author  : lvhongmeng

import (
	"context"
	"dataservices/commons/config"
	"dataservices/commons/constants"
	comErrs "dataservices/commons/errs"
	"dataservices/commons/logger"
	"fmt"
	"sync"

	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"
)

var (
	accountProviderSvcOnce sync.Once
	accountProviderSvcCli  account.AccountAPIServiceClient
)

func GetAccountProviderSvcClient() account.AccountAPIServiceClient {
	return getAccountProviderSvcClient()
}

func getAccountProviderSvcClient() account.AccountAPIServiceClient {
	accountProviderSvcOnce.Do(func() {
		conn, err := tigrpc.NewClientConn(config.Global().GrpcConf.AccountProviderSvcAddr, tigrpc.WithDefault())
		if err != nil {
			panic(fmt.Sprintf("dial account-provider-svc: %s", err))
		}

		accountProviderSvcCli = account.NewAccountAPIServiceClient(conn)
	})

	return accountProviderSvcCli
}

func GetUserByToken(ctx context.Context, req *account.GetUserByTokenRequest) (*account.GetUserByTokenResponse, error) {
	client := getAccountProviderSvcClient()

	resp, e := client.GetUserByToken(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func ListOrgsByUser(ctx context.Context, req *account.ListOrgsByUserRequest) (*account.ListOrgsByUserResponse, error) {
	client := getAccountProviderSvcClient()
	resp, e := client.ListOrgsByUser(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func ListProjectsByOrgsInUser(ctx context.Context, req *account.ListProjectsByOrgsInUserRequest) (*account.ListProjectsByOrgsInUserResponse, error) {
	client := getAccountProviderSvcClient()

	resp, e := client.ListProjectsByOrgsInUser(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func GetAuthUser(ctx context.Context, req *account.GetAuthUserRequest) (*account.GetAuthUserResponse, error) {
	client := getAccountProviderSvcClient()

	resp, e := client.GetAuthUser(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func CreateApiKey(ctx context.Context, req *account.CreateApiKeyReq) (*account.CreateApiKeyRsp, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "CreateApiKey request", zap.Any("request", req))
	resp, e := client.CreateApiKey(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func UpdateApiKey(ctx context.Context, req *account.UpdateApiKeyReq) (*account.UpdateApiKeyRsp, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "UpdateApiKey request", zap.Any("request", req))
	resp, e := client.UpdateApiKey(ctx, req)
	tilog.Info(ctx, "UpdateApiKey response", zap.Any("response", resp), zap.Error(e))
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func DeleteApiKey(ctx context.Context, req *account.DeleteApiKeyReq) (*account.DeleteApiKeyRsp, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "DeleteApiKey request", zap.Any("request", req))
	resp, e := client.DeleteApiKey(ctx, req)
	tilog.Info(ctx, "DeleteApiKey response", zap.Any("response", resp), zap.Error(e))
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func GetApiKeysByResource(ctx context.Context, req *account.GetApiKeysByResourceReq) (*account.GetApiKeysByResourceRsp, error) {
	client := getAccountProviderSvcClient()

	logger.Info(ctx, "GetApiKeysByResource request", zap.Any("request", req))
	resp, e := client.GetApiKeysByResource(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func RecoverApiKeyPermission(ctx context.Context, req *account.RecoverApiKeyPermissionReq) (*account.RecoverApiKeyPermissionRsp, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "RecoverApiKeyPermission request", zap.Any("request", req))
	resp, e := client.RecoverApiKeyPermission(ctx, req)
	tilog.Info(ctx, "RecoverApiKeyPermission response", zap.Any("response", resp), zap.Error(e))
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func RecoverResourceRefAllApiKeyPermission(ctx context.Context, req *account.RecoverResourceRefAllApiKeyPermissionReq) (*account.RecoverResourceRefAllApiKeyPermissionRsp, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "RecoverResourceRefAllApiKeyPermission request", zap.Any("request", req))
	resp, e := client.RecoverResourceRefAllApiKeyPermission(ctx, req)
	tilog.Info(ctx, "RecoverResourceRefAllApiKeyPermission response", zap.Any("response", resp), zap.Error(e))
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func IsHitExp(ctx context.Context, req *account.HitExpReq) (*account.HitExpRsp, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "IsHitExp request", zap.Any("request", req))
	resp, e := client.IsHitExp(ctx, req)
	tilog.Info(ctx, "IsHitExp response", zap.Any("response", resp), zap.Error(e))
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func GetProjectByID(ctx context.Context, req *account.GetProjectByIDRequest) (*account.GetProjectByIDResponse, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "GetProjectByID request", zap.Any("request", req))
	resp, e := client.GetProjectByID(ctx, req)
	tilog.Info(ctx, "GetProjectByID response", zap.Any("response", resp), zap.Error(e))
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	return resp, formatError(ctx, e, resp.BaseResp)
}

func IsEmailHitExp(ctx context.Context, email string, expName string) (bool, error) {
	res, err := IsHitExp(ctx, &account.HitExpReq{
		Email:   email,
		ExpName: expName,
	})
	if err != nil {
		return false, err
	}
	return res.IsHit, nil
}

func IsOrgHitExp(ctx context.Context, orgID uint64, expName string) (bool, error) {
	res, err := IsHitExp(ctx, &account.HitExpReq{
		OrgId:   orgID,
		ExpName: expName,
	})
	if err != nil {
		return false, err
	}
	return res.IsHit, nil
}

func IsOrgHitDedicatedWhitelist(ctx context.Context, orgID uint64, operator string) (bool, error) {
	res, err := IsHitExp(ctx, &account.HitExpReq{
		OrgId:   orgID,
		Email:   operator,
		ExpName: constants.DataserviceEnableDedicatedExpName,
	})
	if err != nil {
		return false, err
	}
	return res.IsHit, nil
}

func GetWhitelist(ctx context.Context, name string) (*account.GetExpConfRsp, error) {
	client := getAccountProviderSvcClient()
	resp, err := client.GetExpConfByName(ctx, &account.GetExpConfReq{
		ExpName: name,
	})
	tilog.Info(ctx, "GetWhitelist response", zap.Any("response", resp), zap.Error(err))
	if err != nil {
		return nil, comErrs.WrapRPCError(err, getCurrentFunc(true))
	}
	return resp, formatError(ctx, err, resp.BaseResp)
}

func HasPermissionForUser(ctx context.Context, req *account.RbacPermissionRequest) (*account.RbacPermissionResponse, error) {
	client := getAccountProviderSvcClient()

	tilog.Info(ctx, "HasPermissionForUser request", zap.Any("request", req))
	resp, e := client.HasPermissionForUser(ctx, req)
	if e != nil {
		return resp, comErrs.WrapRPCError(e, getCurrentFunc(true))
	}
	tilog.Info(ctx, "HasPermissionForUser response", zap.Any("response", resp))
	return resp, formatError(ctx, e, resp.BaseResp)
}
