package server

import (
	"context"
	"errors"
	"io"
	"net/http"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/gitlab"
	gapi "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/gitlab/api"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/gitlab_access/rpc"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modserver"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/grpctool"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/ioz"
	"google.golang.org/grpc"
)

type server struct {
	rpc.UnsafeGitlabAccessServer
	gitLabClient gitlab.ClientInterface
}

func newServer(gitLabClient gitlab.ClientInterface) *server {
	return &server{
		gitLabClient: gitLabClient,
	}
}

func (s *server) MakeRequest(server grpc.BidiStreamingServer[grpctool.HttpRequest, grpctool.HttpResponse]) error {
	rpcAPI := modserver.AgentRPCAPIFromContext(server.Context())
	log := rpcAPI.Log()
	grpc2http := grpctool.InboundGRPCToOutboundHTTP{
		Log: log,
		HandleProcessingError: func(msg string, err error) {
			rpcAPI.HandleProcessingError(log, msg, err)
		},
		HandleIOError: func(msg string, err error) error {
			return rpcAPI.HandleIOError(log, msg, err)
		},
		HTTPDo: s.httpDo,
	}
	return grpc2http.Pipe(server)
}

func (s *server) httpDo(ctx context.Context, header *grpctool.HttpRequest_Header, body io.Reader) (grpctool.DoResponse, error) {
	if header.Request.IsUpgrade() {
		return grpctool.DoResponse{}, errors.New("connection upgrade is not supported")
	}
	var extra rpc.HeaderExtra
	err := header.Extra.UnmarshalTo(&extra)
	if err != nil {
		return grpctool.DoResponse{}, err
	}
	var resp *http.Response
	err = ioz.ReadAllFunc(body, func(body []byte) error {
		resp, err = gapi.MakeModuleRequest( //nolint: bodyclose
			ctx,
			s.gitLabClient,
			modserver.AgentRPCAPIFromContext(ctx).AgentTokenWithType(),
			extra.ModuleName,
			header.Request.Method,
			header.Request.UrlPath,
			header.Request.URLQuery(),
			header.Request.HTTPHeader(),
			body,
		)
		return err
	})
	if err != nil {
		return grpctool.DoResponse{}, err
	}
	return grpctool.DoResponse{
		Resp: resp,
	}, nil
}
