package grpcli

import (
	"context"

	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/common"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/console"
)

type ServerlessSvcGrpcClient interface {
	CreateImport(ctx context.Context, req *console.CreateImportReq) (*console.Import, error)
	ListImports(ctx context.Context, req *console.ListImportsReq) (*console.ListImportsResp, error)
	CancelImport(ctx context.Context, req *console.ImportIds) error
	GetImport(ctx context.Context, req *console.ImportIds) (*console.Import, error)
	Upload(ctx context.Context, req *console.UploadRequest) (*console.UploadResponse, error)
	GetDataIngestionProfile(ctx context.Context, req *console.GetProfileReq) (*console.DataIngestionProfile, error)
	PreviewLocal(ctx context.Context, req *console.ImportsPreviewLocalReq) (*console.ImportsPreviewLocalResp, error)
}

type serverlessSvcGrpcClient struct {
	addr   string
	client console.ImportServiceClient
}

func NewServerlessSvcGrpcClient(ctx context.Context, addr string) ServerlessSvcGrpcClient {
	client := console.NewImportServiceClient(MustBuildGrpcClientConnection(ctx, addr))
	return &serverlessSvcGrpcClient{
		addr:   addr,
		client: client,
	}
}

func (s *serverlessSvcGrpcClient) CreateImport(ctx context.Context, req *console.CreateImportReq) (*console.Import, error) {
	i, err := s.client.CreateImport(ctx, req)
	if err != nil {
		return nil, err
	}
	if i.GetBaseResp().GetErrCode() != 0 {
		return nil, BaseResp2Error(i.GetBaseResp())
	}

	return i, nil
}

func (s *serverlessSvcGrpcClient) ListImports(ctx context.Context, req *console.ListImportsReq) (*console.ListImportsResp, error) {
	res, err := s.client.ListImports(ctx, req)
	if err != nil {
		return nil, err
	}
	if res.GetBaseResp().GetErrCode() != 0 {
		return nil, BaseResp2Error(res.GetBaseResp())
	}

	return res, nil
}

func (s *serverlessSvcGrpcClient) CancelImport(ctx context.Context, req *console.ImportIds) error {
	res, err := s.client.CancelImport(ctx, req)
	if err != nil {
		return err
	}
	if res.GetBaseResp().GetErrCode() != 0 {
		return BaseResp2Error(res.GetBaseResp())
	}
	return nil
}

func (s *serverlessSvcGrpcClient) GetImport(ctx context.Context, req *console.ImportIds) (*console.Import, error) {
	i, err := s.client.GetImport(ctx, req)
	if err != nil {
		return nil, err
	}
	if i.GetBaseResp().GetErrCode() != 0 {
		return nil, BaseResp2Error(i.GetBaseResp())
	}
	return i, nil
}

func (s *serverlessSvcGrpcClient) Upload(ctx context.Context, req *console.UploadRequest) (*console.UploadResponse, error) {
	u, err := s.client.Upload(ctx, req)
	if err != nil {
		return nil, err
	}
	if u.GetBaseResp().GetErrCode() != 0 {
		return nil, BaseResp2Error(u.GetBaseResp())
	}
	return u, nil
}

func (s *serverlessSvcGrpcClient) GetDataIngestionProfile(ctx context.Context, req *console.GetProfileReq) (*console.DataIngestionProfile, error) {
	p, err := s.client.GetDataIngestionProfile(ctx, req)
	if err != nil {
		return nil, err
	}
	if p.GetBaseResp().GetErrCode() != 0 {
		return nil, BaseResp2Error(p.GetBaseResp())
	}
	return p, nil
}

func (s *serverlessSvcGrpcClient) PreviewLocal(ctx context.Context, req *console.ImportsPreviewLocalReq) (*console.ImportsPreviewLocalResp, error) {
	resp, err := s.client.PreviewLocal(ctx, req)
	if err != nil {
		return nil, err
	}
	if resp.GetBaseResp().GetErrCode() != 0 {
		return nil, BaseResp2Error(resp.GetBaseResp())
	}
	return resp, nil
}

func BaseResp2Error(resp *common.BaseResp) error {
	// generate error by specific error code
	switch bizerr.ErrCode(resp.GetErrCode()) {
	case bizerr.ErrCodeParams:
		return errcode.NewInvalidInputErr(errors.New(resp.GetErrMsg()))
	case bizerr.ErrCodeAuth:
		return errcode.NewNotAuthenticatedErr(errors.New(resp.GetErrMsg()))
	case bizerr.ErrCodePermission:
		return errcode.NewForbiddenErr(errors.New(resp.GetErrMsg()))
	case bizerr.ErrCodeRecordNotFound:
		return errcode.NewNotFoundErr(errors.New(resp.GetErrMsg()))
	case bizerr.ErrCodeRecordExist:
		return errcode.NewCodedError(errors.New(resp.GetErrMsg()), errcode.AlreadyExistsCode)
	}

	// generate error by general error code
	switch {
	case bizerr.IsRequestErr(bizerr.ErrCode(resp.GetErrCode())):
		return errcode.NewInvalidInputErr(errors.New(resp.GetErrMsg()))
	case bizerr.IsInternalErr(bizerr.ErrCode(resp.GetErrCode())):
		return errcode.NewInternalErr(errors.New(resp.GetErrMsg()))
	case bizerr.IsBusinessErr(bizerr.ErrCode(resp.GetErrCode())):
		return errcode.NewInvalidInputErr(errors.New(resp.GetErrMsg()))
	}
	return errcode.NewInternalErr(errors.New(resp.GetErrMsg()))
}
