//go:build integ

// Copyright Istio Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ambient

import (
	"fmt"
	"testing"

	"istio.io/istio/pkg/config/constants"
	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/crd"
	"istio.io/istio/pkg/test/framework/components/echo"
	"istio.io/istio/pkg/test/framework/components/echo/check"
	"istio.io/istio/pkg/test/util/retry"
)

const (
	teWasmImageName      = "istio-testing/wasm/header-injector"
	teWasmInjectedHeader = "x-resp-injection"
	teWasmConfigFile     = "testdata/trafficextension-wasm.yaml"
)

type trafficExtensionWasmTestConfig struct {
	desc         string
	name         string
	testHostname string
}

var teWasmGeneration = 0

func applyAndTestTrafficExtensionWasmWithOCI(ctx framework.TestContext, c trafficExtensionWasmTestConfig, path, targetType, targetName string) {
	ctx.NewSubTest("OCI_" + c.desc).Run(func(t framework.TestContext) {
		defer func() {
			teWasmGeneration++
		}()
		mapTagToVersionOrFail(t, "latest", "0.0.1")
		wasmModuleURL := fmt.Sprintf("oci://%v/%v:%v", registry.Address(), teWasmImageName, "latest")
		if err := installTrafficExtensionWasm(t, c.name, wasmModuleURL, "", fmt.Sprintf("g-%d", teWasmGeneration), targetType, targetName, path); err != nil {
			t.Fatalf("failed to install TrafficExtension: %v", err)
		}
		if c.testHostname != "" {
			sendTrafficToHostname(t, check.ResponseHeader(teWasmInjectedHeader, "0.0.1"), c.testHostname)
		} else {
			sendTraffic(t, check.ResponseHeader(teWasmInjectedHeader, "0.0.1"))
		}
	})
}

func resetTrafficExtensionWasmConfig(ctx framework.TestContext, filterName, path string) {
	ctx.NewSubTest("Delete TrafficExtension " + filterName).Run(func(t framework.TestContext) {
		if err := uninstallTrafficExtensionWasm(t, filterName, path); err != nil {
			t.Fatal(err)
		}
		sendTraffic(t, check.ResponseHeader(teWasmInjectedHeader, ""), retry.Converge(2))
	})
}

func applyTrafficExtensionWasmConfigFile(ctx framework.TestContext, ns string, args map[string]any, path string) error {
	return ctx.ConfigIstio().EvalFile(ns, args, path).Apply()
}

func installTrafficExtensionWasm(ctx framework.TestContext, filterName, wasmModuleURL, imagePullPolicy, filterVersion, targetType, targetName, path string) error {
	kind, group, name := getTargetRefValues(targetType, targetName)

	args := map[string]any{
		"TrafficExtensionName": filterName,
		"TestWasmModuleURL":   wasmModuleURL,
		"FilterVersion":       filterVersion,
		"TargetKind":          kind,
		"TargetGroup":         group,
		"TargetName":          name,
	}

	if len(imagePullPolicy) != 0 {
		args["ImagePullPolicy"] = imagePullPolicy
	}

	if err := applyTrafficExtensionWasmConfigFile(ctx, apps.Namespace.Name(), args, path); err != nil {
		return err
	}

	return nil
}

func uninstallTrafficExtensionWasm(ctx framework.TestContext, filterName, path string) error {
	args := map[string]any{
		"TrafficExtensionName": filterName,
	}
	if err := ctx.ConfigIstio().EvalFile(apps.Namespace.Name(), args, path).Delete(); err != nil {
		return err
	}
	return nil
}

// TestTrafficExtension_WasmConfigurations tests WASM TrafficExtension on different targets in ambient mode
func TestTrafficExtension_WasmConfigurations(t *testing.T) {
	framework.NewTest(t).
		Run(func(t framework.TestContext) {
			testCases := []struct {
				desc         string
				name         string
				testHostname string
				targetType   string
				targetName   string
			}{
				{
					desc:         "Configure WASM filter for gateway",
					name:         "gateway-wasm-trafficextension",
					testHostname: fmt.Sprintf("%s-gateway-istio.%s.svc.cluster.local", GetTarget().ServiceName(), apps.Namespace.Name()),
					targetType:   "gateway",
					targetName:   fmt.Sprintf("%s-gateway", GetTarget().(echo.Instances).ServiceName()),
				},
				{
					desc:       "Configure WASM filter for waypoint",
					name:       "waypoint-wasm-trafficextension",
					targetType: "gateway",
					targetName: constants.DefaultNamespaceWaypoint,
				},
				{
					desc:       "Configure WASM filter for specific service",
					name:       "service-wasm-trafficextension",
					targetType: "service",
					targetName: GetTarget().Instances().ServiceName(),
				},
			}

			for _, tc := range testCases {
				if tc.name == "gateway-wasm-trafficextension" {
					crd.DeployGatewayAPIOrSkip(t)
					args := map[string]any{
						"To": GetTarget().Instances(),
					}
					t.ConfigIstio().EvalFile(apps.Namespace.Name(), args, "testdata/gateway-api.yaml").ApplyOrFail(t)
				}

				applyAndTestTrafficExtensionWasmWithOCI(t, trafficExtensionWasmTestConfig{
					desc:         tc.desc,
					name:         tc.name,
					testHostname: tc.testHostname,
				}, teWasmConfigFile, tc.targetType, tc.targetName)

				resetTrafficExtensionWasmConfig(t, tc.name, teWasmConfigFile)
			}
		})
}
