package tempcorecli

import (
	"context"
	"errors"
	"strings"
	"time"

	"braces.dev/errtrace"
	"github.com/sercand/kuberesolver/v5"
	coreapi "github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	tigrpcclient "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"google.golang.org/grpc"

	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/tempcore"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	grpcutil "github.com/tidbcloud/tidb-management-service/utilities/grpc"
)

func NewClient(cfg *config.CoreSvcService) (tempcore.Client, error) {
	if cfg == nil || cfg.Local {
		return &fakeImpl{}, nil
	}

	var (
		conn *grpc.ClientConn
		err  error
	)

	interceptors := tigrpcclient.WithUnaryInterceptors(
		grpcutil.ClientPayloadLogInterceptor,
		grpcutil.TimeoutInterceptor(time.Duration(cfg.WaitTimeoutInMilliSec)*time.Millisecond),
	)

	opts := []tigrpcclient.Option{tigrpcclient.WithPrometheus(), tigrpcclient.WithTracing(), interceptors}
	if strings.HasPrefix(cfg.Url, "kubernetes://") {
		kuberesolver.RegisterInCluster()
		opts = append(opts, tigrpcclient.WithLoadBalance())
	}

	if conn, err = tigrpcclient.NewClientConn(cfg.Url, opts...); err != nil {
		return nil, errtrace.Wrap(err)
	}
	coreCli := coreapi.NewCoreServiceClient(conn)

	c := clientImpl{coreCli: coreCli}
	return &c, nil
}

type clientImpl struct {
	coreCli coreapi.CoreServiceClient
}

func (c clientImpl) ListClusters(ctx context.Context, req *coreapi.ListClustersReq) (*coreapi.ListClustersResp, error) {
	resp, err := c.coreCli.ListClusters(ctx, req)
	if err != nil {
		return resp, errtrace.Wrap(bizerr.WrapGrpcError(err, "ListClusters"))
	}
	if resp.BaseResp.ErrCode != 0 {
		return resp, errtrace.Wrap(bizerr.New(bizerr.ErrCode(resp.BaseResp.ErrCode), "%s", resp.BaseResp.ErrMsg))
	}
	return resp, nil
}

func (c clientImpl) GetCluster(ctx context.Context, clusterID uint64) (*coreapi.Cluster, error) {
	req := &coreapi.ListClustersReq{
		Filter: &coreapi.ClusterFilter{
			ClusterIds: []uint64{clusterID},
		},
	}
	resp, err := c.ListClusters(ctx, req)
	if err != nil {
		return nil, errtrace.Wrap(err)
	}
	if len(resp.Clusters) == 0 {
		return nil, errtrace.Wrap(bizerr.WrapError(errors.New("NotFound"), "cluster %d not found", clusterID))
	}
	return resp.Clusters[clusterID], nil
}
