// Copyright (c) 2021 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 manager

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"testing"
	"time"

	ctesting "github.com/gitpod-io/gitpod/common-go/testing"
	"github.com/gitpod-io/gitpod/ws-manager/pkg/clock"
	corev1 "k8s.io/api/core/v1"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestActOnPodEvent(t *testing.T) {
	type actOnPodEventResult struct {
		Actions []actRecord `json:"actions"`
		Error   string      `json:"error,omitempty"`
	}

	test := ctesting.FixtureTest{
		T:    t,
		Path: "testdata/status_*.json",
		GoldPath: func(fn string) string {
			res := fmt.Sprintf("%s.golden", strings.TrimSuffix(fn, filepath.Ext(fn)))
			res = strings.ReplaceAll(res, "/status_", "/actOnPodEvent_")
			return res
		},
		Test: func(t *testing.T, input interface{}) interface{} {
			fixture := input.(*workspaceObjects)
			manager := Manager{
				Clientset: fake.NewClientBuilder().Build(),
				clock:     clock.LogicalOnly(),
			}
			status, serr := manager.getWorkspaceStatus(*fixture)
			if serr != nil {
				t.Skipf("skipping due to status computation error: %v", serr)
			}

			var rec actRecorder
			err := actOnPodEvent(context.Background(), &rec, &manager, status, fixture)

			result := actOnPodEventResult{Actions: rec.Records}
			if err != nil {
				result.Error = err.Error()
			}
			return &result
		},
		Fixture: func() interface{} { return &workspaceObjects{} },
		Gold:    func() interface{} { return &actOnPodEventResult{} },
	}
	test.Run()
}

type actRecord struct {
	Func   string
	Params map[string]interface{}
}

type actRecorder struct {
	Records []actRecord
}

func (r *actRecorder) waitForWorkspaceReady(ctx context.Context, pod *corev1.Pod) (err error) {
	r.Records = append(r.Records, actRecord{
		Func: "waitForWorkspaceReady",
		Params: map[string]interface{}{
			"pod": pod,
		},
	})
	return nil
}

func (r *actRecorder) stopWorkspace(ctx context.Context, workspaceID string, gracePeriod time.Duration) (err error) {
	r.Records = append(r.Records, actRecord{
		Func: "stopWorkspace",
		Params: map[string]interface{}{
			"workspaceID": workspaceID,
			"gracePeriod": gracePeriod,
		},
	})
	return nil
}

func (r *actRecorder) markWorkspace(ctx context.Context, workspaceID string, annotations ...*annotation) error {
	r.Records = append(r.Records, actRecord{
		Func: "markWorkspace",
		Params: map[string]interface{}{
			"workspaceID": workspaceID,
			"annotations": annotations,
		},
	})
	return nil
}

func (r *actRecorder) clearInitializerFromMap(podName string) {
	r.Records = append(r.Records, actRecord{
		Func: "clearInitializerFromMap",
		Params: map[string]interface{}{
			"podName": podName,
		},
	})
}

func (r *actRecorder) initializeWorkspaceContent(ctx context.Context, pod *corev1.Pod) (err error) {
	r.Records = append(r.Records, actRecord{
		Func: "initializeWorkspaceContent",
		Params: map[string]interface{}{
			"pod": pod,
		},
	})
	return nil
}

func (r *actRecorder) finalizeWorkspaceContent(ctx context.Context, wso *workspaceObjects) {
	r.Records = append(r.Records, actRecord{
		Func: "finalizeWorkspaceContent",
		Params: map[string]interface{}{
			"wso": wso,
		},
	})
}

func (r *actRecorder) modifyFinalizer(ctx context.Context, workspaceID string, finalizer string, add bool) error {
	r.Records = append(r.Records, actRecord{
		Func: "modifyFinalizer",
		Params: map[string]interface{}{
			"workspaceID": workspaceID,
			"finalizer":   finalizer,
			"add":         add,
		},
	})
	return nil
}

func (r *actRecorder) deleteWorkspaceSecrets(ctx context.Context, podName string) error {
	r.Records = append(r.Records, actRecord{
		Func: "deleteWorkspaceSecrets",
		Params: map[string]interface{}{
			"podName": podName,
		},
	})
	return nil
}

var _ actingManager = &actRecorder{}
