package agent

import (
	"context"
	"errors"
	"testing"
	"time"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/testing/testhelpers"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/testing/testlogger"
	gomock "go.uber.org/mock/gomock"
	"k8s.io/client-go/kubernetes/fake"
)

type scanResponse struct {
	uuids []string
	err   error
}

const (
	namespace1                = "namespace1"
	namespace2                = "namespace2"
	gitlabAgentNamespace      = "gitlab-agent"
	gitlabAgentServiceAccount = "gitlab-agent"
	podName                   = "Pod name"
	uuid1                     = "uuid1"
	uuid2                     = "uuid2"
	uuid3                     = "uuid3"
	uuid4                     = "uuid4"
	scannerTimeout            = 5 * time.Minute
	reportMaxSizeBytes        = 100000000 // 100MB
)

var testAgentKey = testhelpers.AgentkKey1

func TestScanner_Scan(t *testing.T) {
	tests := []struct {
		name                      string
		targetNamespaces          []string
		namespaceScanResponses    map[string]scanResponse
		expectVulnResolutionUUIDs []string
	}{
		{
			name:             "Single namespace scan succeeds and triggers vulnerability resolution",
			targetNamespaces: []string{namespace1},
			namespaceScanResponses: map[string]scanResponse{
				namespace1: {uuids: []string{uuid1, uuid2}, err: nil},
			},
			expectVulnResolutionUUIDs: []string{uuid1, uuid2},
		},
		{
			name:             "Single namespace scan fails and skips vulnerability resolution",
			targetNamespaces: []string{namespace1},
			namespaceScanResponses: map[string]scanResponse{
				namespace1: {uuids: nil, err: errors.New("scan error")},
			},
		},
		{
			name:             "Multiple namespace scans succeed and trigger vulnerability resolution",
			targetNamespaces: []string{namespace1, namespace2},
			namespaceScanResponses: map[string]scanResponse{
				namespace1: {uuids: []string{uuid1, uuid2}, err: nil},
				namespace2: {uuids: []string{uuid3, uuid4}, err: nil},
			},
			expectVulnResolutionUUIDs: []string{uuid3, uuid4, uuid1, uuid2},
		},
		{
			name:             "Multiple namespace scans with one failure skip vulnerability resolution",
			targetNamespaces: []string{namespace1, namespace2},
			namespaceScanResponses: map[string]scanResponse{
				namespace1: {uuids: []string{uuid1, uuid2}, err: nil},
				namespace2: {uuids: nil, err: errors.New("scan error")},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			mockReporter := NewMockReporter(ctrl)
			if len(tt.expectVulnResolutionUUIDs) > 0 {
				// If vulnerability resolution is expected, expect ResolveVulnerabilities
				// to be called with specific UUIDs
				mockReporter.EXPECT().
					ResolveVulnerabilities(gomock.Any(), gomock.InAnyOrder(tt.expectVulnResolutionUUIDs)).
					Return(nil)
			} else {
				// If vulnerability resolution is NOT expected, expect ResolveVulnerabilities to not be called
				mockReporter.EXPECT().
					ResolveVulnerabilities(gomock.Any(), gomock.Any()).
					Times(0)
			}

			mockNamespaceScanner := NewMockNamespaceScanner(ctrl)
			for targetNamespace, response := range tt.namespaceScanResponses {
				mockNamespaceScanner.EXPECT().
					scan(gomock.Any(), gomock.Any(), targetNamespace, mockReporter, gomock.Any()).
					Return(response.uuids, response.err)
			}

			kubeClientset := fake.NewSimpleClientset()
			scanner := newScanner(
				testlogger.New(t),
				kubeClientset,
				gitlabAgentNamespace,
				testAgentKey,
				&defaultResourceRequirements,
				tt.targetNamespaces,
				false,
				scannerTimeout,
				reportMaxSizeBytes,
				[]string{kindDeployment},
			)

			scanner.scan(context.Background(), mockNamespaceScanner, mockReporter)
		})
	}
}
