package agent

import (
	"net/http"
	"strings"

	"github.com/hashicorp/nomad/nomad/structs"
)

// ServiceRegistrationListRequest performs a listing of service registrations
// using the structs.ServiceRegistrationListRPCMethod RPC endpoint and is
// callable via the /v1/services HTTP API.
func (s *HTTPServer) ServiceRegistrationListRequest(
	resp http.ResponseWriter, req *http.Request) (interface{}, error) {

	// The endpoint only supports GET requests.
	if req.Method != http.MethodGet {
		return nil, CodedError(http.StatusMethodNotAllowed, ErrInvalidMethod)
	}

	// Set up the request args and parse this to ensure the query options are
	// set.
	args := structs.ServiceRegistrationListRequest{}

	if s.parse(resp, req, &args.Region, &args.QueryOptions) {
		return nil, nil
	}

	// Perform the RPC request.
	var reply structs.ServiceRegistrationListResponse
	if err := s.agent.RPC(structs.ServiceRegistrationListRPCMethod, &args, &reply); err != nil {
		return nil, err
	}

	setMeta(resp, &reply.QueryMeta)

	if reply.Services == nil {
		reply.Services = make([]*structs.ServiceRegistrationListStub, 0)
	}
	return reply.Services, nil
}

// ServiceRegistrationRequest is callable via the /v1/service/ HTTP API and
// handles service reads and individual service registration deletions.
func (s *HTTPServer) ServiceRegistrationRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {

	// Grab the suffix of the request, so we can further understand it.
	reqSuffix := strings.TrimPrefix(req.URL.Path, "/v1/service/")

	// Split the request suffix in order to identify whether this is a lookup
	// of a service, or whether this includes a service and service identifier.
	suffixParts := strings.Split(reqSuffix, "/")

	switch len(suffixParts) {
	case 1:
		// This endpoint only supports GET.
		if req.Method != http.MethodGet {
			return nil, CodedError(http.StatusMethodNotAllowed, ErrInvalidMethod)
		}

		// Ensure the service ID is not an empty string which is possible if
		// the caller requested "/v1/service/service-name/"
		if suffixParts[0] == "" {
			return nil, CodedError(http.StatusBadRequest, "missing service name")
		}

		return s.serviceGetRequest(resp, req, suffixParts[0])

	case 2:
		// This endpoint only supports DELETE.
		if req.Method != http.MethodDelete {
			return nil, CodedError(http.StatusMethodNotAllowed, ErrInvalidMethod)
		}

		// Ensure the service ID is not an empty string which is possible if
		// the caller requested "/v1/service/service-name/"
		if suffixParts[1] == "" {
			return nil, CodedError(http.StatusBadRequest, "missing service id")
		}

		return s.serviceDeleteRequest(resp, req, suffixParts[1])

	default:
		return nil, CodedError(http.StatusBadRequest, "invalid URI")
	}
}

// serviceGetRequest performs a reading of service registrations by name using
// the structs.ServiceRegistrationGetServiceRPCMethod RPC endpoint.
func (s *HTTPServer) serviceGetRequest(
	resp http.ResponseWriter, req *http.Request, serviceName string) (interface{}, error) {

	args := structs.ServiceRegistrationByNameRequest{ServiceName: serviceName}
	if s.parse(resp, req, &args.Region, &args.QueryOptions) {
		return nil, nil
	}

	var reply structs.ServiceRegistrationByNameResponse
	if err := s.agent.RPC(structs.ServiceRegistrationGetServiceRPCMethod, &args, &reply); err != nil {
		return nil, err
	}
	setMeta(resp, &reply.QueryMeta)

	if reply.Services == nil {
		reply.Services = make([]*structs.ServiceRegistration, 0)
	}
	return reply.Services, nil
}

// serviceDeleteRequest performs a reading of service registrations by name using
// the structs.ServiceRegistrationDeleteByIDRPCMethod RPC endpoint.
func (s *HTTPServer) serviceDeleteRequest(
	resp http.ResponseWriter, req *http.Request, serviceID string) (interface{}, error) {

	args := structs.ServiceRegistrationDeleteByIDRequest{ID: serviceID}
	s.parseWriteRequest(req, &args.WriteRequest)

	var reply structs.ServiceRegistrationDeleteByIDResponse
	if err := s.agent.RPC(structs.ServiceRegistrationDeleteByIDRPCMethod, &args, &reply); err != nil {
		return nil, err
	}
	setIndex(resp, reply.Index)
	return nil, nil
}
