// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 v1

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	v1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1"
)

func buildGatewayProxyValidator(t *testing.T, objects ...runtime.Object) *GatewayProxyCustomValidator {
	t.Helper()

	scheme := runtime.NewScheme()
	require.NoError(t, clientgoscheme.AddToScheme(scheme))
	require.NoError(t, v1alpha1.AddToScheme(scheme))

	builder := fake.NewClientBuilder().WithScheme(scheme)
	if len(objects) > 0 {
		builder = builder.WithRuntimeObjects(objects...)
	}

	return NewGatewayProxyCustomValidator(builder.Build())
}

func newGatewayProxy() *v1alpha1.GatewayProxy {
	return &v1alpha1.GatewayProxy{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "demo",
			Namespace: "default",
		},
		Spec: v1alpha1.GatewayProxySpec{
			Provider: &v1alpha1.GatewayProxyProvider{
				Type: v1alpha1.ProviderTypeControlPlane,
				ControlPlane: &v1alpha1.ControlPlaneProvider{
					Service: &v1alpha1.ProviderService{Name: "control-plane"},
					Auth: v1alpha1.ControlPlaneAuth{
						Type: v1alpha1.AuthTypeAdminKey,
						AdminKey: &v1alpha1.AdminKeyAuth{
							ValueFrom: &v1alpha1.AdminKeyValueFrom{
								SecretKeyRef: &v1alpha1.SecretKeySelector{
									Name: "admin-key",
									Key:  "token",
								},
							},
						},
					},
				},
			},
		},
	}
}

func TestGatewayProxyValidator_MissingService(t *testing.T) {
	gp := newGatewayProxy()
	gp.Spec.Provider.ControlPlane.Auth.AdminKey = nil
	validator := buildGatewayProxyValidator(t)

	warnings, err := validator.ValidateCreate(context.Background(), gp)
	require.NoError(t, err)
	require.Len(t, warnings, 1)
	require.Contains(t, warnings[0], "Service 'default/control-plane' not found")
}

func TestGatewayProxyValidator_MissingAdminSecret(t *testing.T) {
	gp := newGatewayProxy()
	service := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "control-plane",
			Namespace: "default",
		},
	}
	validator := buildGatewayProxyValidator(t, service)

	warnings, err := validator.ValidateCreate(context.Background(), gp)
	require.NoError(t, err)
	require.Len(t, warnings, 1)
	require.Contains(t, warnings[0], "Secret 'default/admin-key' not found")
}

func TestGatewayProxyValidator_MissingAdminSecretKey(t *testing.T) {
	gp := newGatewayProxy()
	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "admin-key",
			Namespace: "default",
		},
		Data: map[string][]byte{
			"wrong": []byte("value"),
		},
	}

	service := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "control-plane",
			Namespace: "default",
		},
	}

	validator := buildGatewayProxyValidator(t, secret, service)

	warnings, err := validator.ValidateCreate(context.Background(), gp)
	require.NoError(t, err)
	require.Len(t, warnings, 1)
	require.Contains(t, warnings[0], "Secret key 'token' not found")
}

func TestGatewayProxyValidator_NoWarnings(t *testing.T) {
	gp := newGatewayProxy()
	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "admin-key",
			Namespace: "default",
		},
		Data: map[string][]byte{
			"token": []byte("value"),
		},
	}
	service := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "control-plane",
			Namespace: "default",
		},
	}

	validator := buildGatewayProxyValidator(t, secret, service)

	warnings, err := validator.ValidateCreate(context.Background(), gp)
	require.NoError(t, err)
	require.Empty(t, warnings)
}
