package server

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

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/autoflow/encryption"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/autoflow_codec_server"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/modserver"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/httpz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/ioz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/nettool"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/tlstool"
	"go.temporal.io/sdk/converter"
)

type Factory struct {
}

func (f *Factory) New(config *modserver.Config) (modserver.Module, error) {
	autoflowCfg := config.Config.Autoflow
	if autoflowCfg == nil {
		config.Log.Info("AutoFlow module is not enabled in config, not starting codec server")
		return nil, nil
	}

	cfg := autoflowCfg.Temporal.WorkflowDataEncryption
	if cfg == nil {
		config.Log.Info("AutoFlow with Workflow data encryption module is not enabled in config, not starting codec server")
		return nil, nil
	}

	secretKey, err := ioz.ReadSecretAES256Key(cfg.SecretKeyFile)
	if err != nil {
		return nil, err
	}

	codec := encryption.NewCodec(secretKey)

	listenCfg := cfg.CodecServer.Listen
	certFile := listenCfg.CertificateFile
	keyFile := listenCfg.KeyFile
	var listener func(context.Context) (net.Listener, error)

	tlsConfig, err := tlstool.MaybeServerConfig(certFile, keyFile)
	if err != nil {
		return nil, err
	}
	if tlsConfig != nil {
		// This enables HTTP/2 when the listener is configured with TLS.
		tlsConfig.NextProtos = []string{httpz.TLSNextProtoH2, httpz.TLSNextProtoH1}
		listener = func(ctx context.Context) (net.Listener, error) {
			// stdlib's HTTP server requires tls.Conn for HTTP/2, so we cannot wrap connections here.
			// See https://pkg.go.dev/net/http#Server.Serve.
			return nettool.TLSListenWithOSTCPKeepAlive(ctx, *listenCfg.Network, listenCfg.Address, tlsConfig)
		}
	} else {
		listener = func(ctx context.Context) (net.Listener, error) {
			return nettool.ListenWithOSTCPKeepAlive(ctx, *listenCfg.Network, listenCfg.Address)
		}
	}

	provider, err := newOIDCProvider(
		context.Background(),
		http.DefaultTransport,
		cfg.CodecServer.TemporalOidcUrl,
		*autoflowCfg.Temporal.Namespace,
		cfg.CodecServer.AuthorizedUserEmails,
	)
	if err != nil {
		return nil, err
	}

	m := &module{
		log: config.Log,
		server: server{
			listenerGracePeriod: listenCfg.ListenGracePeriod.AsDuration(),
			shutdownGracePeriod: listenCfg.ShutdownGracePeriod.AsDuration(),
			allowedOrigins:      []string{cfg.CodecServer.TemporalWebUiUrl},
			provider:            provider,
			decodeHandler:       converter.NewPayloadCodecHTTPHandler(codec),
		},
		listener: listener,
	}
	return m, nil
}

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