package grpcli

import (
	"context"

	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/dataflow-service/pkg/log"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/tiinf/sdk/rbac/helperimpl"
	"go.uber.org/zap"
)

type AccountSvcGrpcClient interface {
	GetClient() account.AccountAPIServiceClient

	GetOrgByID(ctx context.Context, orgId uint64) (*account.Org, bizerr.BizError)
	ListOrgMapByIDs(ids []uint64) (map[uint64]*account.Org, bizerr.BizError)

	GetProjectByID(ctx context.Context,
		req *account.GetProjectByIDRequest) (*account.GetProjectByIDResponse, bizerr.BizError)

	ListProjects(ctx context.Context,
		req *account.ProjectFilter) (*account.ListProjectsResponse, bizerr.BizError)

	GetOrCreateSystemUser(ctx context.Context,
		req *account.GetOrCreateSystemUserRequest) (*account.User, bizerr.BizError)

	GetUserByToken(ctx context.Context,
		req *account.GetUserByTokenRequest) (*account.GetUserByTokenResponse, bizerr.BizError)

	GetAuthUser(ctx context.Context,
		req *account.GetAuthUserRequest) (*account.GetAuthUserResponse, bizerr.BizError)
}

type accountSvcGrpcClientImpl struct {
	addr   string
	client account.AccountAPIServiceClient
}

func NewAccountSvcGrpcClient(ctx context.Context, addr string) AccountSvcGrpcClient {
	logger := log.GetLogger(ctx)
	logger.Info("initializing account-svc client", zap.String("addr", addr))
	conn := MustBuildGrpcClientConnection(ctx, addr)
	accountSvcCli := account.NewAccountAPIServiceClient(conn)
	helperimpl.DefaultCachedPermissionHelper.SetEndpoint(addr)
	return &accountSvcGrpcClientImpl{
		addr:   addr,
		client: accountSvcCli,
	}
}

func (c *accountSvcGrpcClientImpl) GetClient() account.AccountAPIServiceClient {
	return c.client
}

func (c *accountSvcGrpcClientImpl) GetUserByToken(
	ctx context.Context, req *account.GetUserByTokenRequest,
) (*account.GetUserByTokenResponse, bizerr.BizError) {
	resp, err := c.client.GetUserByToken(ctx, req)
	if err != nil {
		return resp, bizerr.WrapGrpcError(err, "GetUserByToken")
	}
	if code := resp.BaseResp.ErrCode; code != 0 {
		return resp, bizerr.Restore(bizerr.ErrCode(code), resp.BaseResp.ErrMsg)
	}
	return resp, nil
}

func (c *accountSvcGrpcClientImpl) GetAuthUser(
	ctx context.Context, req *account.GetAuthUserRequest,
) (*account.GetAuthUserResponse, bizerr.BizError) {
	resp, err := c.client.GetAuthUser(ctx, req)
	if err != nil {
		return resp, bizerr.WrapGrpcError(err, "GetAuthUser")
	}
	if code := resp.BaseResp.ErrCode; code != 0 {
		return resp, bizerr.Restore(bizerr.ErrCode(code), resp.BaseResp.ErrMsg)
	}
	return resp, nil
}

func (c *accountSvcGrpcClientImpl) GetOrgByID(ctx context.Context, orgId uint64) (*account.Org, bizerr.BizError) {
	req := &account.GetOrgByIDRequest{
		Id: orgId,
	}
	resp, err := c.client.GetOrgByID(ctx, req)
	if err != nil {
		return nil, bizerr.WrapGrpcError(err, "GetOrgByID")
	}
	if code := resp.BaseResp.ErrCode; code != 0 {
		return nil, bizerr.Restore(bizerr.ErrCode(code), resp.BaseResp.ErrMsg)
	}

	if resp.Org == nil || resp.Org.Id == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "org id=%d is not found", orgId)
	}
	return resp.Org, nil
}

func (c *accountSvcGrpcClientImpl) GetProjectByID(
	ctx context.Context, req *account.GetProjectByIDRequest) (*account.GetProjectByIDResponse, bizerr.BizError) {
	resp, err := c.client.GetProjectByID(ctx, req)
	if err != nil {
		return resp, bizerr.WrapGrpcError(err, "GetProjectByID")
	}
	if code := resp.BaseResp.ErrCode; code != 0 {
		return resp, bizerr.Restore(bizerr.ErrCode(code), resp.BaseResp.ErrMsg)
	}
	return resp, nil
}

func (c *accountSvcGrpcClientImpl) ListProjects(ctx context.Context,
	req *account.ProjectFilter) (*account.ListProjectsResponse, bizerr.BizError) {
	resp, err := c.client.ListProjects(ctx, req)
	if err != nil {
		return resp, bizerr.WrapGrpcError(err, "ListProjects")
	}
	if code := resp.BaseResp.ErrCode; code != 0 {
		return resp, bizerr.Restore(bizerr.ErrCode(code), resp.BaseResp.ErrMsg)
	}
	return resp, nil
}

func (c *accountSvcGrpcClientImpl) ListOrgMapByIDs(ids []uint64) (map[uint64]*account.Org, bizerr.BizError) {
	if len(ids) == 0 {
		return nil, nil
	}

	ctx := context.Background()
	request := &account.OrgFilter{
		Ids: ids,
	}

	response, err := c.client.ListOrgs(ctx, request)
	if err != nil {
		return nil, bizerr.WrapGrpcError(err, "ListOrgMapByIDs")
	}
	if code := response.BaseResp.ErrCode; code != 0 {
		return nil, bizerr.Restore(bizerr.ErrCode(code), response.BaseResp.ErrMsg)
	}

	result := make(map[uint64]*account.Org)
	for _, org := range response.Orgs {
		result[org.Id] = org
	}
	return result, nil
}

func (c *accountSvcGrpcClientImpl) GetOrCreateSystemUser(
	ctx context.Context, req *account.GetOrCreateSystemUserRequest,
) (*account.User, bizerr.BizError) {
	resp, err := c.client.GetOrCreateSystemUser(ctx, req)
	if err != nil {
		return nil, bizerr.WrapGrpcError(err, "GetOrCreateSystemUser")
	}
	return resp.User, nil
}
