package grpcli

import (
	"context"
	"fmt"

	"github.com/tidbcloud/config-center/proto/config_center"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/serverless-service/internal/config"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"
	"google.golang.org/grpc/status"
)

// EssentialChangefeedClusterType uses the value introduced by config-center PR #256.
// TODO: replace it with config_center.ClusterType_CHANGEFEED_ESSENTIAL after the
// current TiKV client can compile with gRPC v1.67.0 required by the new proto module.
const EssentialChangefeedClusterType config_center.ClusterType = 9

type ConfigCenterClient interface {
	SearchDefaultConfigurationTemplate(
		ctx context.Context,
		req *config_center.SearchDefaultConfigurationTemplateRequest,
	) (*config_center.ConfigurationTemplate, bizerr.BizError)
	UpdateClusterMeta(
		ctx context.Context,
		req *config_center.UpdateClusterMetaRequest,
	) (*config_center.UpdateClusterMetaResponse, bizerr.BizError)
	GetClusterConfigurationFile(
		ctx context.Context,
		req *config_center.GetClusterConfigurationFileRequest,
	) (*config_center.ConfigurationFile, bizerr.BizError)
	UpdateClusterConfigurationFile(
		ctx context.Context,
		req *config_center.UpdateClusterConfigurationFileRequest,
	) (*config_center.ConfigurationFile, bizerr.BizError)
}

type configCenterClient struct {
	client config_center.ConfigCenterServiceClient
}

func NewConfigCenterClient() ConfigCenterClient {
	ctx := context.Background()
	addr := config.Global().SvcAddrConfig.ConfigCenterAddr
	conn, err := tigrpc.NewClientConn(addr, tigrpc.WithDefault())
	log.Info(ctx, "Initializing config-center client", zap.Any("err", err), zap.String("addr", addr))
	if err != nil {
		panic(fmt.Sprintf("dial config-center: %s", err))
	}
	return &configCenterClient{client: config_center.NewConfigCenterServiceClient(conn)}
}

func (c *configCenterClient) SearchDefaultConfigurationTemplate(
	ctx context.Context,
	req *config_center.SearchDefaultConfigurationTemplateRequest,
) (*config_center.ConfigurationTemplate, bizerr.BizError) {
	resp, err := c.client.SearchDefaultConfigurationTemplate(ctx, req)
	if err != nil {
		return nil, configCenterError(err)
	}
	return resp, nil
}

func (c *configCenterClient) UpdateClusterMeta(
	ctx context.Context,
	req *config_center.UpdateClusterMetaRequest,
) (*config_center.UpdateClusterMetaResponse, bizerr.BizError) {
	resp, err := c.client.UpdateClusterMeta(ctx, req)
	if err != nil {
		return nil, configCenterError(err)
	}
	return resp, nil
}

func (c *configCenterClient) GetClusterConfigurationFile(
	ctx context.Context,
	req *config_center.GetClusterConfigurationFileRequest,
) (*config_center.ConfigurationFile, bizerr.BizError) {
	resp, err := c.client.GetClusterConfigurationFile(ctx, req)
	if err != nil {
		return nil, configCenterError(err)
	}
	return resp, nil
}

func (c *configCenterClient) UpdateClusterConfigurationFile(
	ctx context.Context,
	req *config_center.UpdateClusterConfigurationFileRequest,
) (*config_center.ConfigurationFile, bizerr.BizError) {
	resp, err := c.client.UpdateClusterConfigurationFile(ctx, req)
	if err != nil {
		return nil, configCenterError(err)
	}
	return resp, nil
}

func configCenterError(err error) bizerr.BizError {
	if s, ok := status.FromError(err); ok {
		return wrapGrpcError(s.Err())
	}
	return wrapGrpcError(err)
}
