package regionserver

import (
	"context"
	"net/http"

	"github.com/go-resty/resty/v2"
	clusterdedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"

	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func (s *regionServerCliImpl) CreateTidbGroup(
	ctx context.Context,
	region *entity.Region,
	req *clusterdedicatedv1.CreateTidbGroupRequest,
) (*clusterdedicatedv1.TidbGroup, bizerr.BizError) {
	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["CreateTidbGroup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.TidbGroup.ClusterId)
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

	var out clusterdedicatedv1.TidbGroup
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionServerCliImpl) GetTidbGroup(
	ctx context.Context,
	region *entity.Region,
	req *clusterdedicatedv1.GetTidbGroupRequest,
) (*clusterdedicatedv1.TidbGroup, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetTidbGroup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.ClusterId)
			request.SetPathParam(tidbGroupIdPathParameter, req.GetTidbGroupId())
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out clusterdedicatedv1.TidbGroup
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionServerCliImpl) ListTidbGroups(
	ctx context.Context,
	region *entity.Region,
	req *clusterdedicatedv1.ListTidbGroupsRequest,
) (*clusterdedicatedv1.ListTidbGroupsResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["ListTidbGroups"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.ClusterId)
			params := map[string]string{
				clusterIdParameter: req.GetClusterId(),
			}
			request.SetQueryParams(params)
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out clusterdedicatedv1.ListTidbGroupsResponse
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionServerCliImpl) UpdateTidbGroup(
	ctx context.Context,
	region *entity.Region,
	req *clusterdedicatedv1.UpdateTidbGroupRequest,
) (*clusterdedicatedv1.TidbGroup, bizerr.BizError) {
	if req.TidbGroup == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group is nil")
	}
	// The following fields are not allowed to be updated, set them to nil.
	// TODO: remove this two fields after the api is updated
	req.TidbGroup.Tidb = nil    // leave it empty
	req.TidbGroup.Tiproxy = nil // leave it empty

	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}

	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodPatch,
		operationMap["UpdateTidbGroup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.ClusterId)
			request.SetPathParam(tidbGroupIdPathParameter, req.GetTidbGroupId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

	var out clusterdedicatedv1.TidbGroup
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionServerCliImpl) DeleteTidbGroup(
	ctx context.Context,
	region *entity.Region,
	req *clusterdedicatedv1.DeleteTidbGroupRequest,
) bizerr.BizError {
	tidbGroup, bizErr := s.GetTidbGroup(ctx, region, &clusterdedicatedv1.GetTidbGroupRequest{
		ClusterId:   req.ClusterId,
		TidbGroupId: req.TidbGroupId,
	})
	if bizErr != nil {
		return bizErr
	}
	actualRequest := &metadata.ExpectRequest[string]{
		TenantId:   "", // leave it empty
		ProjectId:  "", // leave it empty
		ClusterId:  tidbGroup.ClusterId,
		ResourceId: "", // leave it empty
	}
	if err := metadata.Validate(ctx, actualRequest); err != nil {
		return bizerr.WrapError(err, "validate failed")
	}

	_, err := s.callRegion(
		ctx,
		region,
		http.MethodDelete,
		operationMap["DeleteTidbGroup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.ClusterId)
			request.SetPathParam(tidbGroupIdPathParameter, req.GetTidbGroupId())
			return request
		},
	)
	if err != nil {
		return err
	}

	return nil
}
