// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package wsmanager

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"

	"sigs.k8s.io/e2e-framework/pkg/envconf"
	"sigs.k8s.io/e2e-framework/pkg/features"

	csapi "github.com/gitpod-io/gitpod/content-service/api"
	agent "github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api"
	"github.com/gitpod-io/gitpod/test/pkg/integration"
	wsmanapi "github.com/gitpod-io/gitpod/ws-manager/api"
	corev1 "k8s.io/api/core/v1"
)

const (
	SECRET_NAME  = "USER_SECRET"
	SECRET_VALUE = "a9upr238"
)

func TestProtectedSecrets(t *testing.T) {
	f := features.New("protected_secrets").WithLabel("component", "ws-manager").Assess("can use protected secrets", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
		t.Parallel()

		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
		defer cancel()

		api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
		t.Cleanup(func() {
			api.Done(t)
		})

		swr := func(req *wsmanapi.StartWorkspaceRequest) error {
			req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
				Name:  SECRET_NAME,
				Value: SECRET_VALUE,
			})

			req.Spec.Initializer = &csapi.WorkspaceInitializer{
				Spec: &csapi.WorkspaceInitializer_Git{
					Git: &csapi.GitInitializer{
						RemoteUri:        "https://github.com/gitpod-io/empty",
						CheckoutLocation: "empty",
						Config:           &csapi.GitConfig{},
					},
				},
			}

			req.Spec.WorkspaceLocation = "empty"
			return nil
		}

		ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(swr))
		if err != nil {
			t.Fatalf("cannot launch a workspace: %q", err)
		}

		t.Cleanup(func() {
			sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute)
			defer scancel()

			sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client())
			defer sapi.Done(t)

			_, err = stopWs(true, sapi)
			if err != nil {
				t.Errorf("cannot stop workspace: %q", err)
			}
		})

		k8sClient := cfg.Client()
		var wsPod corev1.Pod
		if err := k8sClient.Resources().Get(context.Background(), "ws-"+ws.Req.Id, cfg.Namespace(), &wsPod); err != nil {
			t.Fatal(err)
		}

		assertEnvSuppliedBySecret(t, &wsPod, SECRET_NAME)

		rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
			integration.WithInstanceID(ws.Req.Id),
			integration.WithContainer("workspace"),
			integration.WithWorkspacekitLift(true),
		)
		if err != nil {
			t.Fatal(err)
		}

		assertEnvAvailableInWs(t, rsa)

		integration.DeferCloser(t, closer)
		defer rsa.Close()

		return ctx
	}).Feature()

	testEnv.Test(t, f)
}

func assertEnvSuppliedBySecret(t *testing.T, wsPod *corev1.Pod, secretEnv string) {
	for _, c := range wsPod.Spec.Containers {
		if c.Name != "workspace" {
			continue
		}

		for _, env := range c.Env {
			if env.Name == secretEnv {
				if env.Value != "" {
					t.Fatalf("environment variable has plain text value")
				}

				if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil {
					t.Fatalf("environment variable value is not supplied by secret")
				}

				if env.ValueFrom.SecretKeyRef.Name != wsPod.Name {
					t.Fatalf("expected environment variable values are not supplied by secret %s", wsPod.Name)
				}
			}
		}
	}
}

func assertEnvAvailableInWs(t *testing.T, rsa *integration.RpcClient) {
	var grepResp agent.ExecResponse
	err := rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
		Dir:     prebuildLogPath,
		Command: "bash",
		Args: []string{
			"-c",
			fmt.Sprintf("env | grep %s", SECRET_NAME),
		},
	}, &grepResp)

	if err != nil {
		t.Fatal(err)
	}

	expected := fmt.Sprintf("%s=%s", SECRET_NAME, SECRET_VALUE)
	if strings.TrimSpace(grepResp.Stdout) != expected {
		t.Fatalf("expected environment variable to be %s, but was %s", expected, grepResp.Stdout)
	}
}
