package clientv2

import (
	"context"
	"crypto/tls"
	"net/http"
	"net/url"

	"github.com/pingcap/errors"
	scalerpb "github.com/tidbcloud/aws-shared-provider/pkg/tidbworker/proto/scaler"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type Client interface {
	// Ping checks if the manager is operating normally.
	Ping(ctx context.Context) error
	gcClient
	gcV2Client
	bgTaskClient
	remoteQueryClient
	ttlClient
	autoAnalyzeClient
}

var _ Client = &client{}

type Option struct {
	KeyspaceName string
	KeyspaceID   uint32
	TiDBPool     string
	ScalerAddr   string
	TLSConfig    *tls.Config
}

type client struct {
	keyspaceName string
	KeyspaceID   uint32
	tidbPool     string

	grpcClient scalerpb.ScalerClient
	httpClient *http.Client
	scalerAddr string
}

// NewClientWithContext creates a new client with the given context.
func NewClientWithContext(ctx context.Context, cliOption *Option, dialOptions ...grpc.DialOption) (Client, error) {
	// Handle URL.
	scalerURL, err := url.Parse(cliOption.ScalerAddr)
	if err != nil {
		return nil, errors.Trace(err)
	}
	scalerURL.Scheme = "http"
	httpClient := &http.Client{}
	// Setup TLS if necessary.
	if cliOption.TLSConfig != nil {
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(cliOption.TLSConfig)))
		httpClient.Transport = &http.Transport{TLSClientConfig: cliOption.TLSConfig}
		scalerURL.Scheme = "https"
	}
	// GRPC only need the host part for the url.
	conn, err := grpc.DialContext(ctx, scalerURL.Host, dialOptions...)
	if err != nil {
		return nil, err
	}

	return &client{
		keyspaceName: cliOption.KeyspaceName,
		KeyspaceID:   cliOption.KeyspaceID,
		tidbPool:     cliOption.TiDBPool,
		grpcClient:   scalerpb.NewScalerClient(conn),
		httpClient:   httpClient,
		scalerAddr:   scalerURL.String(), // Use the full url for http requests.
	}, nil
}

func (c *client) Ping(ctx context.Context) error {
	req := &scalerpb.PingRequest{}
	resp, err := c.grpcClient.Ping(ctx, req)
	if err != nil {
		return err
	}
	if resp.GetError() != nil {
		return errors.Errorf("ping failed: %s", resp.GetError().GetMessage())
	}
	return nil
}
