package handler

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"strings"

	s2sLog "code.justin.tv/sse/malachai/pkg/log"
	s2sCaller "code.justin.tv/sse/malachai/pkg/s2s/caller"
	"github.com/pkg/errors"

	rpcinfra "code.justin.tv/eventbus/controlplane/infrastructure/rpc"
	"code.justin.tv/eventbus/controlplane/internal/ldap/injector"
)

const (
	prodControlPlaneEndpoint    = "https://controlplane.prod.eventbus.twitch.a2z.com"
	stagingControlPlaneEndpoint = "https://controlplane.staging.eventbus.twitch.a2z.com"
	devControlPlaneEndpoint     = "http://httpserver:8888"
)

// RegisterEvent uses a Twirp client to submit the event stream information to the eventbus controlplane
func RegisterEvent(ctx context.Context, event rpcinfra.EventDefinition) (string, error) {
	// TODO: I'm sure there's some handy logging we _could_ do with the context, but need to investigate
	//
	// Ideas for using the context:
	// * Include the git commit hash which corresponds to the lambda invocation
	// * Timestamp
	// * Committer name/ldap/etc
	// * Link to Jenkins build or PR

	endpoint := controlPlaneEndpoint()

	rt := http.DefaultTransport
	if os.Getenv("S2S_DISABLED") == "" {
		cfg := &s2sCaller.Config{
			DisableStatsClient: true,
		}

		t, err := s2sCaller.NewRoundTripper(s2sName(), cfg, &s2sLog.NoOpLogger{})
		if err != nil {
			return "", errors.Wrap(err, "failed to initialize s2s roundtripper")
		}
		rt = t
	}

	httpClient := &http.Client{
		Transport: injector.NewLDAPHeaderInjector("controlplane-updater", rt),
	}

	client := rpcinfra.NewInfrastructureProtobufClient(endpoint, httpClient)

	_, err := client.RegisterEventDefinitions(ctx, &rpcinfra.RegisterEventDefinitionsReq{
		EventDefinitions: []*rpcinfra.EventDefinition{
			&event,
		},
	})
	if err != nil {
		return "", errors.Wrap(err, "could not call controlplane.RegisterEventDefinition")
	}

	return fmt.Sprintf("received event with type '%s' and display_name '%s'", event.EventType, event.DisplayName), nil
}

// Try to extract the endpoint from the environment variable ENVIRONMENT
// If it is present, it must be one of a few valid values. If it is invalid or
// not set, default to local
func controlPlaneEndpoint() string {
	env := environment()
	switch env {
	case "staging":
		return stagingControlPlaneEndpoint
	case "production", "prod":
		return prodControlPlaneEndpoint
	default:
		return devControlPlaneEndpoint
	}
}

// A tiny wrapper around os.Getenv so we can override it for testing
func environment() string {
	return strings.ToLower(os.Getenv("ENVIRONMENT"))
}

func s2sName() string {
	// These strings are registered in the internal dashboard at https://dashboard.internal.justin.tv/s2s/services

	if envName := environment(); envName == "production" || envName == "prod" {
		return "eventbus-controlplane-updater-production"
	}
	return "eventbus-controlplane-updater-staging"
}
