package server

import (
	"context"
	_ "embed"
	"net/http"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/api"
	kubernetes_api_rpc "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/kubernetes_api/rpc"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/managed_resources"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/managed_resources/rpc"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modserver"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/fieldz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/grpctool"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/logz"
	"google.golang.org/grpc"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
)

//go:embed default_template.yaml
var defaultTemplate []byte

const (
	maxTemplateFileSize = 32 * 1024
)

type Factory struct {
}

func (f *Factory) New(config *modserver.Config) (modserver.Module, error) {
	restCfg := &rest.Config{
		UserAgent: config.KASNameVersion,
		QPS:       -1,
	}
	mapper := newMapper()
	srv := &server{
		gitaly: config.Gitaly,
		agentClient: func(agentKey api.AgentKey, rpcAPI modserver.RPCAPI) (dynamic.Interface, error) {
			log := rpcAPI.Log().With(logz.AgentKey(agentKey))
			httpClient := &http.Client{
				Transport: &grpctool.HTTPRoundTripperToOutboundGRPC{
					HTTP2GRPC: grpctool.HTTPToOutboundGRPC{
						Log: log,
						HandleProcessingErrorFunc: func(msg string, err error) {
							rpcAPI.HandleProcessingError(log, msg, err, fieldz.AgentKey(agentKey))
						},
						CheckHeader: func(statusCode int32, header http.Header) error {
							return nil
						},
					},
					NewClient: func(ctx context.Context) (grpctool.HTTPRequestClient, error) {
						return kubernetes_api_rpc.NewKubernetesApiClient(config.AgentConnPool(agentKey)).
							MakeRequest(ctx, grpc.WaitForReady(true))
					},
				},
				CheckRedirect: func(req *http.Request, via []*http.Request) error {
					return http.ErrUseLastResponse
				},
			}
			return dynamic.NewForConfigAndClient(restCfg, httpClient)
		},
		defaultTemplate: &managed_resources.EnvironmentTemplate{
			Name: managed_resources.DefaultTemplateName,
			Data: defaultTemplate,
		},
		mapper: mapper,
		renderer: &managed_resources.Renderer{
			Mapper:    mapper,
			Validator: config.Validator,
		},
		fieldManager:        api.FieldManager,
		maxTemplateFileSize: maxTemplateFileSize,
	}
	rpc.RegisterProvisionerServer(config.APIServer, srv)
	return nil, nil
}

func (f *Factory) Name() string {
	return managed_resources.ModuleName
}
