package profiles

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/linkerd/linkerd2/controller/api/util"
	sp "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
	pb "github.com/linkerd/linkerd2/controller/gen/public"
	"github.com/linkerd/linkerd2/pkg/k8s"
	"github.com/linkerd/linkerd2/pkg/protohttp"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestTapToServiceProfile(t *testing.T) {
	name := "service-name"
	namespace := "service-namespace"
	clusterDomain := "service-cluster.local"
	tapDuration := 5 * time.Second
	routeLimit := 20

	params := util.TapRequestParams{
		Resource:  "deploy/" + name,
		Namespace: namespace,
	}

	tapReq, err := util.BuildTapByResourceRequest(params)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	event1 := util.CreateTapEvent(
		&pb.TapEvent_Http{
			Event: &pb.TapEvent_Http_RequestInit_{

				RequestInit: &pb.TapEvent_Http_RequestInit{
					Id: &pb.TapEvent_Http_StreamId{
						Base: 1,
					},
					Authority: "",
					Path:      "/emojivoto.v1.VotingService/VoteFire",
					Method: &pb.HttpMethod{
						Type: &pb.HttpMethod_Registered_{
							Registered: pb.HttpMethod_POST,
						},
					},
				},
			},
		},
		map[string]string{},
		pb.TapEvent_INBOUND,
	)

	event2 := util.CreateTapEvent(
		&pb.TapEvent_Http{
			Event: &pb.TapEvent_Http_RequestInit_{

				RequestInit: &pb.TapEvent_Http_RequestInit{
					Id: &pb.TapEvent_Http_StreamId{
						Base: 2,
					},
					Authority: "",
					Path:      "/my/path/hi",
					Method: &pb.HttpMethod{
						Type: &pb.HttpMethod_Registered_{
							Registered: pb.HttpMethod_GET,
						},
					},
				},
			},
		},
		map[string]string{},
		pb.TapEvent_INBOUND,
	)

	kubeAPI, err := k8s.NewFakeAPI()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	ts := httptest.NewServer(http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			for _, event := range []pb.TapEvent{event1, event2} {
				event := event // pin
				err = protohttp.WriteProtoToHTTPResponse(w, &event)
				if err != nil {
					t.Fatalf("Unexpected error: %v", err)
				}
			}
		}),
	)
	defer ts.Close()
	kubeAPI.Config.Host = ts.URL

	expectedServiceProfile := sp.ServiceProfile{
		TypeMeta: serviceProfileMeta,
		ObjectMeta: metav1.ObjectMeta{
			Name:      name + "." + namespace + ".svc." + clusterDomain,
			Namespace: namespace,
		},
		Spec: sp.ServiceProfileSpec{
			Routes: []*sp.RouteSpec{
				{
					Name: "GET /my/path/hi",
					Condition: &sp.RequestMatch{
						PathRegex: `/my/path/hi`,
						Method:    "GET",
					},
				},
				{
					Name: "POST /emojivoto.v1.VotingService/VoteFire",
					Condition: &sp.RequestMatch{
						PathRegex: `/emojivoto\.v1\.VotingService/VoteFire`,
						Method:    "POST",
					},
				},
			},
		},
	}

	actualServiceProfile, err := tapToServiceProfile(kubeAPI, tapReq, namespace, name, clusterDomain, tapDuration, routeLimit)
	if err != nil {
		t.Fatalf("Failed to create ServiceProfile: %v", err)
	}

	err = ServiceProfileYamlEquals(actualServiceProfile, expectedServiceProfile)
	if err != nil {
		t.Fatalf("ServiceProfiles are not equal: %v", err)
	}
}
