//go:generate ${GOBIN}/mockgen -destination regional_server_mock.go -package=mgmthttpcli ${GO_MODULE}/pkg/infra/clients/mgmthttpcli RegionServer
package mgmthttpcli

import (
	"context"
	"fmt"

	"github.com/go-resty/resty/v2"
	regionalv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"google.golang.org/protobuf/proto"
)

type RegionServer interface {
	ListK8SInformation(ctx context.Context, req *regionalv1.ListK8SInformationRequest) (*regionalv1.K8SInformationList, error)
	GetK8sInformation(ctx context.Context, req *regionalv1.GetK8SInformationRequest) (*regionalv1.K8SInformation, error)

	GetUpgradeOperation(ctx context.Context, req *regionalv1.GetUpgradeOperationRequest) (*regionalv1.UpgradeOperation, error)
	CreateUpgradeOperation(ctx context.Context, req *regionalv1.CreateUpgradeOperationRequest) error
	UpdateUpgradeOperation(ctx context.Context, req *regionalv1.UpdateUpgradeOperationRequest) error
	PatchUpgradeOperationAnnotations(ctx context.Context, req *regionalv1.PatchUpgradeOperationAnnotationsRequest) error
	DeleteUpgradeOperation(ctx context.Context, req *regionalv1.DeleteUpgradeOperationRequest) error

	ListK8sResources(ctx context.Context, req *regionalv1.ListK8SResourcesRequest) (*regionalv1.ListK8SResourcesResponse, error)
}

type regionServer struct {
	core          *resty.Client
	region        string
	cloudProvider string
}

func (c *regionServer) ListK8SInformation(ctx context.Context, req *regionalv1.ListK8SInformationRequest) (*regionalv1.K8SInformationList, error) {
	var resp regionalv1.K8SInformationList

	restyReq := c.core.R().
		SetContext(ctx).
		SetQueryParams(map[string]string{
			"version": req.Version,
		}).
		SetBody(proto.Message(req)).
		SetResult(&resp)

	_, err := c.do(resty.MethodGet, "/api/dedicated/v1/k8sInfos", restyReq)
	return &resp, err
}

func (c *regionServer) GetK8sInformation(ctx context.Context, req *regionalv1.GetK8SInformationRequest) (*regionalv1.K8SInformation, error) {
	var resp regionalv1.K8SInformation

	restyReq := c.core.R().
		SetContext(ctx).
		SetPathParam("name", req.Name).
		SetBody(proto.Message(req)).
		SetResult(&resp)

	_, err := c.do(resty.MethodGet, "/api/dedicated/v1/k8sInfos/{name}", restyReq)
	return &resp, err
}

func (c *regionServer) GetUpgradeOperation(ctx context.Context, req *regionalv1.GetUpgradeOperationRequest) (*regionalv1.UpgradeOperation, error) {
	var resp regionalv1.UpgradeOperation

	restyReq := c.core.R().
		SetContext(ctx).
		SetPathParam("name", req.Name).
		SetBody(proto.Message(req)).
		SetResult(&resp)

	_, err := c.do(resty.MethodGet, "/api/dedicated/v1/upgradeOperations/{name}", restyReq)
	return &resp, err
}

func (c *regionServer) CreateUpgradeOperation(ctx context.Context, req *regionalv1.CreateUpgradeOperationRequest) error {
	restyReq := c.core.R().
		SetContext(ctx).
		SetBody(proto.Message(req))

	_, err := c.do(resty.MethodPost, "/api/dedicated/v1/upgradeOperations", restyReq)
	return err
}

func (c *regionServer) UpdateUpgradeOperation(ctx context.Context, req *regionalv1.UpdateUpgradeOperationRequest) error {
	restyReq := c.core.R().
		SetContext(ctx).
		SetPathParam("name", req.Name).
		SetBody(proto.Message(req))

	_, err := c.do(resty.MethodPatch, "/api/dedicated/v1/upgradeOperations/{name}", restyReq)
	return err
}

func (c *regionServer) PatchUpgradeOperationAnnotations(ctx context.Context, req *regionalv1.PatchUpgradeOperationAnnotationsRequest) error {
	restyReq := c.core.R().
		SetContext(ctx).
		SetPathParam("name", req.Name).
		SetBody(proto.Message(req))

	_, err := c.do(resty.MethodPatch, "/api/dedicated/v1/upgradeOperations/{name}/annotations", restyReq)
	return err
}

func (c *regionServer) DeleteUpgradeOperation(ctx context.Context, req *regionalv1.DeleteUpgradeOperationRequest) error {
	restyReq := c.core.R().
		SetContext(ctx).
		SetPathParam("name", req.Name).
		SetBody(proto.Message(req))

	_, err := c.do(resty.MethodDelete, "/api/dedicated/v1/upgradeOperations/{name}", restyReq)
	return err
}

func (c *regionServer) ListK8sResources(ctx context.Context, req *regionalv1.ListK8SResourcesRequest) (*regionalv1.ListK8SResourcesResponse, error) {
	var resp regionalv1.ListK8SResourcesResponse

	restyReq := c.core.R().
		SetContext(ctx).
		SetBody(proto.Message(req)).
		SetResult(&resp)

	_, err := c.do(resty.MethodPost, "/api/dedicated/v1/k8sResources:list", restyReq)
	return &resp, err
}

func (c *regionServer) do(method, apiPath string, request *resty.Request) (*resty.Response, error) {
	prefix := fmt.Sprintf("proxy/regions/%s/cloudProviders/%s", c.region, c.cloudProvider)
	url := prefix + apiPath
	resp, err := request.Execute(method, url)
	if err != nil {
		return nil, err
	}

	return resp, nil
}
