package cli

import (
	"context"
	"time"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"

	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/pkg/config"
	grpcutil "github.com/tidbcloud/tidb-management-service/utilities/grpc"
)

type RegionalProxyClient interface {
	GetClusterDataByIds(
		ctx context.Context,
		ids []string,
	) ([]*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, bool)
	BatchSet(ctx context.Context, clusters []*regionalserverlessv1.ServerlessRegionalServiceClusterInfo) (bool, error)
	Delete(ctx context.Context, id string) (bool, error)
	DeleteAll(ctx context.Context) (bool, error)
}

type regionalProxyClientImpl struct {
	regionalCacheCli globalserverlessv1.RegionalServerProxyServiceClient
}

func NewRegionalProxyClientImpl() RegionalProxyClient {
	if !config.Global().RegionalServiceConfig.RegionalProxyServiceConfig.Enabled {
		tilog.PlainInfo("disable regional proxy service")
		return nil
	}

	addr := config.Global().RegionalServiceConfig.RegionalProxyServiceConfig.Addr
	conn, err := tigrpc.NewClientConn(
		addr,
		tigrpc.WithPrometheus(),
		tigrpc.WithTracing(),
		tigrpc.WithUnaryInterceptors(
			grpcutil.ClientPayloadLogInterceptor,
			grpcutil.TimeoutInterceptor(
				time.Duration(
					config.Global().RegionalServiceConfig.RegionalProxyServiceConfig.WaitTimeoutInMilliSec,
				)*time.Millisecond,
			),
		),
	)
	tilog.PlainInfo("Initializing regional proxy service client", zap.Any("err", err), zap.String("addr", addr))
	if err != nil {
		tilog.PlainError("dial regional-proxy-svc", zap.String("addr", addr), zap.Error(err))
		return nil
	}

	return &regionalProxyClientImpl{
		regionalCacheCli: globalserverlessv1.NewRegionalServerProxyServiceClient(conn),
	}
}

func (s *regionalProxyClientImpl) GetClusterDataByIds(
	ctx context.Context,
	ids []string,
) ([]*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, bool) {
	resp, err := s.regionalCacheCli.GetDataByIds(ctx, &globalserverlessv1.RegionalServerProxyServiceGetDataByIdsRequest{
		Ids: ids,
	})
	if err != nil {
		return nil, false
	}

	return resp.Clusters, resp.Exist
}

func (s *regionalProxyClientImpl) BatchSet(
	ctx context.Context,
	clusters []*regionalserverlessv1.ServerlessRegionalServiceClusterInfo,
) (bool, error) {
	resp, err := s.regionalCacheCli.BatchSet(ctx, &globalserverlessv1.RegionalServerProxyServiceBatchSetRequest{
		Clusters: clusters,
	})
	if err != nil {
		return false, err
	}

	return resp.Ok, nil
}

func (s *regionalProxyClientImpl) Delete(ctx context.Context, id string) (bool, error) {
	resp, err := s.regionalCacheCli.Delete(ctx, &globalserverlessv1.RegionalServerProxyServiceDeleteRequest{
		Id: id,
	})
	if err != nil {
		return false, err
	}

	return resp.Ok, nil
}

func (s *regionalProxyClientImpl) DeleteAll(ctx context.Context) (bool, error) {
	resp, err := s.regionalCacheCli.DeleteAll(ctx, &globalserverlessv1.RegionalServerProxyServiceDeleteAllRequest{})
	if err != nil {
		return false, err
	}

	return resp.Ok, nil
}

type fakeRegionalProxyClientImpl struct {
	svc globalserverlessv1.RegionalServerProxyServiceServer
}

func NewFakeRegionalProxyClientImpl(svc globalserverlessv1.RegionalServerProxyServiceServer) RegionalProxyClient {
	return &fakeRegionalProxyClientImpl{
		svc: svc,
	}
}

func (c *fakeRegionalProxyClientImpl) GetClusterDataByIds(
	ctx context.Context,
	ids []string,
) ([]*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, bool) {
	resp, err := c.svc.GetDataByIds(ctx, &globalserverlessv1.RegionalServerProxyServiceGetDataByIdsRequest{
		Ids: ids,
	})
	if err != nil {
		return nil, false
	}

	return resp.Clusters, resp.Exist
}

func (c *fakeRegionalProxyClientImpl) BatchSet(
	ctx context.Context,
	clusters []*regionalserverlessv1.ServerlessRegionalServiceClusterInfo,
) (bool, error) {
	resp, err := c.svc.BatchSet(ctx, &globalserverlessv1.RegionalServerProxyServiceBatchSetRequest{
		Clusters: clusters,
	})
	if err != nil {
		return false, err
	}

	return resp.Ok, nil
}

func (c *fakeRegionalProxyClientImpl) Delete(ctx context.Context, id string) (bool, error) {
	resp, err := c.svc.Delete(ctx, &globalserverlessv1.RegionalServerProxyServiceDeleteRequest{
		Id: id,
	})
	if err != nil {
		return false, err
	}

	return resp.Ok, nil
}

func (c *fakeRegionalProxyClientImpl) DeleteAll(ctx context.Context) (bool, error) {
	resp, err := c.svc.DeleteAll(ctx, &globalserverlessv1.RegionalServerProxyServiceDeleteAllRequest{})
	if err != nil {
		return false, err
	}

	return resp.Ok, nil
}
