package regionalserver

import (
	"context"
	"net/http"
	"strconv"

	"github.com/go-resty/resty/v2"
	clusterngv1beta "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

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

func (s *regionalServerCliImpl) CreateSharedPool(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.CreateSharedPoolRequest) (*clusterngv1beta.SharedPool, 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["CreateSharedPool"],
		func(request *resty.Request) *resty.Request {
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) GetSharedPool(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetSharedPoolRequest) (*clusterngv1beta.SharedPool, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetSharedPool"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			return request
		},
	)
	if err != nil {
		if resp != nil && resp.StatusCode() == http.StatusNotFound {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "shared pool not found")
		}
		return nil, err
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) DeleteSharedPool(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.DeleteSharedPoolRequest) bizerr.BizError {
	_, bizErr := s.GetSharedPool(ctx, region, &clusterngv1beta.GetSharedPoolRequest{
		SharedPoolId: req.GetSharedPoolId(),
	})
	if bizErr != nil {
		if bizErr.ErrCode() == bizerr.ErrCodeRecordNotFound {
			tilog.Warn(ctx, "shared pool not found when deleting, skip deletion",
				zap.String("shared_pool_id", req.GetSharedPoolId()))
			return nil
		}
		return bizErr
	}

	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodDelete,
		operationMap["DeleteSharedPool"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			request.SetQueryParams(map[string]string{
				"pruneDataplane": strconv.FormatBool(req.PruneDataplane),
			})
			return request
		},
	)
	if err != nil {
		if resp != nil && resp.StatusCode() == http.StatusNotFound {
			return bizerr.New(bizerr.ErrCodeRecordNotFound, "shared pool not found")
		}
		return err
	}

	return nil
}

func (s *regionalServerCliImpl) ListSharedPools(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.ListSharedPoolsRequest) (*clusterngv1beta.ListSharedPoolsResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["ListSharedPools"],
		func(request *resty.Request) *resty.Request {
			request.SetQueryParamsFromValues(map[string][]string{
				"ids": req.GetIds(),
			})
			return request
		},
	)
	if err != nil {
		return nil, err
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) UpdateSharedPoolVcpusLimit(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.UpdateSharedPoolVcpusLimitRequest) (*clusterngv1beta.SharedPool, 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["UpdateSharedPoolVcpusLimit"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) GetSharedPoolVcpusUsage(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetSharedPoolVcpusUsageRequest) (*clusterngv1beta.GetSharedPoolVcpusUsageResponse, bizerr.BizError) {
	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetSharedPoolVcpusUsage"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) GetSharedPoolScaleConstraints(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetSharedPoolScaleConstraintsRequest) (*clusterngv1beta.GetSharedPoolScaleConstraintsResponse, bizerr.BizError) {
	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetSharedPoolScaleConstraints"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) ScaleSharedPool(ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.ScaleSharedPoolRequest) (*clusterngv1beta.SharedPool, 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["ScaleSharedPool"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) GetSharedPoolProvisionConfiguration(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetSharedPoolProvisionConfigurationRequest,
) (*clusterngv1beta.SharedPoolProvisionConfiguration, bizerr.BizError) {
	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetSharedPoolProvisionConfiguration"],
		func(request *resty.Request) *resty.Request {
			return request
		},
	)
	if berr != nil {
		return nil, mapUnimplementedRuntimeMethod(resp, berr)
	}

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

	return &out, nil
}

func (s *regionalServerCliImpl) EvaluateSharedPoolNetworkCapacity(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.EvaluateSharedPoolNetworkCapacityRequest,
) (*clusterngv1beta.EvaluateSharedPoolNetworkCapacityResponse, 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["EvaluateSharedPoolNetworkCapacity"],
		func(request *resty.Request) *resty.Request {
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}
	// callRegion accepts Conflict for legacy create operations. Capacity
	// evaluation is read-only, so every non-2xx response is a dependency error.
	if resp.StatusCode() < http.StatusOK || resp.StatusCode() >= http.StatusMultipleChoices {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "regional network capacity evaluation failed")
	}

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

func (s *regionalServerCliImpl) UpdateSharedPoolCloudResourceTags(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.UpdateSharedPoolCloudResourceTagsRequest,
) (*clusterngv1beta.SharedPool, 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["UpdateSharedPoolCloudResourceTags"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

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

	return &out, nil
}
