package api

import (
	"context"
	"fmt"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/api"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/gitlab"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/server_api"
	"google.golang.org/protobuf/proto"
)

const (
	AgentkInfoAPIPath           = "/api/v4/internal/agents/agentk/agent_info"
	AgentwInfoAPIPath           = "/api/v4/internal/agents/agentw/agent_info"
	RunnerControllerInfoAPIPath = "/api/v4/internal/ci/agents/runnerc/info"
	RunnerInfoAPIPath           = "/api/v4/internal/ci/agents/runner/info"
)

func GetAgentInfo(ctx context.Context, client gitlab.ClientInterface, agentTokenWithType api.AgentTokenWithType, opts ...gitlab.DoOption) (server_api.AgentInfo, error) {
	switch agentTokenWithType.Type { //nolint:exhaustive
	case api.AgentTypeKubernetes:
		return getInfo[GetAgentkInfoResponse](ctx, AgentkInfoAPIPath, client, agentTokenWithType, opts...)
	case api.AgentTypeWorkspace:
		return getInfo[GetAgentwInfoResponse](ctx, AgentwInfoAPIPath, client, agentTokenWithType, opts...)
	case api.AgentTypeRunnerController:
		return getInfo[GetRunnerControllerInfoResponse](ctx, RunnerControllerInfoAPIPath, client, agentTokenWithType, opts...)
	case api.AgentTypeRunner:
		return getInfo[GetRunnerInfoResponse](ctx, RunnerInfoAPIPath, client, agentTokenWithType, opts...)
	default:
		return nil, fmt.Errorf("unknown agent token type while fetching agent info: %v", agentTokenWithType.Type)
	}
}

func getInfo[T any, R interface {
	*T
	proto.Message
	ToAgentInfo() server_api.AgentInfo
}](ctx context.Context, apiPath string, client gitlab.ClientInterface, agentTokenWithType api.AgentTokenWithType, opts ...gitlab.DoOption) (server_api.AgentInfo, error) {
	resp := R(new(T))
	err := client.Do(ctx,
		joinOpts(opts,
			gitlab.WithPath(apiPath),
			gitlab.WithAgentTokenAndType(agentTokenWithType),
			gitlab.WithJWT(true),
			gitlab.WithResponseHandler(gitlab.ProtoJSONResponseHandler(resp)),
		)...,
	)
	if err != nil {
		return nil, err
	}
	return resp.ToAgentInfo(), nil
}
