package proxy

import (
	"context"
	"io"
	"net/http"
	"strings"
	"testing"

	"github.com/onsi/gomega"

	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/o11y"
	o11yurls "github.com/pingcap-inc/clinic/pkg/o11y/urls"
)

func routerTrimLogQueryPrefix(s string) string {
	return strings.TrimPrefix(s, "/log/query")
}

func TestBuildProxyForLogsTargetPathJoinAndStripRoutingParams(t *testing.T) {
	g := gomega.NewWithT(t)

	_, targetHost, modifyFunc, _, err := buildProxyForLogsTarget( //nolint:bodyclose
		context.Background(),
		&o11yurls.LogsTarget{
			LogQueryURL: "https://logs.example.com/loki/self-monitoring/",
			O11yCluster: &o11y.ClusterInfo{
				BizType:  "dev-devtier",
				TenantID: "dev-devtier",
				Vendor:   "aws",
				Region:   "us-west-2",
			},
		},
		&db.Cluster{
			DeployType:   o11y.SharedType,
			DeployTypeV2: o11y.DeployTypeV2Starter,
			Provider:     "aws",
			Region:       "us-west-2",
		},
		routerTrimLogQueryPrefix,
	)
	g.Expect(err).To(gomega.Succeed())
	g.Expect(targetHost).To(gomega.Equal("logs.example.com"))

	for _, tc := range []struct {
		name string
		path string
	}{
		{name: "query_range", path: "/log/query/loki/api/v1/query_range"},
		{name: "labels", path: "/log/query/loki/api/v1/labels"},
		{name: "label values", path: "/log/query/loki/api/v1/label/container/values"},
		{name: "series", path: "/log/query/loki/api/v1/series"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			r, _ := http.NewRequest(
				http.MethodGet,
				"http://localhost:8080"+tc.path+"?cluster_id=cluster-1&var-cluster_id=cluster-2&var-tidb_cluster=cluster-3&control_plane_info=cp&var-control_plane_info=cp2&var-project_id=project&query="+
					"%7Bcontainer%3D%22tidb%22%7D",
				nil,
			)
			r.Header.Set("x-clinic-cluster-id", "cluster-1")
			r.Header.Set(ControlPlaneInfoVariable, "cp")
			r.URL.RawPath = tc.path
			modifiedRequest := modifyFunc(r)

			g.Expect(modifiedRequest.URL.Path).To(gomega.Equal("/loki/self-monitoring" + strings.TrimPrefix(tc.path, "/log/query")))
			g.Expect(modifiedRequest.URL.RawPath).To(gomega.Equal("/loki/self-monitoring" + strings.TrimPrefix(tc.path, "/log/query")))
			g.Expect(modifiedRequest.URL.Query().Get("cluster_id")).To(gomega.Equal(""))
			g.Expect(modifiedRequest.URL.Query().Get("var-cluster_id")).To(gomega.Equal(""))
			g.Expect(modifiedRequest.URL.Query().Get(VarTiDBClusterVariable)).To(gomega.Equal(""))
			g.Expect(modifiedRequest.URL.Query().Get(ControlPlaneInfoVariable)).To(gomega.Equal(""))
			g.Expect(modifiedRequest.URL.Query().Get(VarControlPlaneInfoVariable)).To(gomega.Equal(""))
			g.Expect(modifiedRequest.URL.Query().Get(VarProjectIDVariable)).To(gomega.Equal(""))
			g.Expect(modifiedRequest.Header.Get("x-clinic-cluster-id")).To(gomega.Equal(""))
			g.Expect(modifiedRequest.Header.Get(ControlPlaneInfoVariable)).To(gomega.Equal(""))
			g.Expect(modifiedRequest.URL.Query().Get("query")).To(gomega.Equal(`{container="tidb"}`))
			g.Expect(modifiedRequest.Host).To(gomega.Equal("logs.example.com"))
		})
	}
}

func TestBuildProxyForLogsTargetPreservesPostBody(t *testing.T) {
	g := gomega.NewWithT(t)

	_, _, modifyFunc, _, err := buildProxyForLogsTarget( //nolint:bodyclose
		context.Background(),
		&o11yurls.LogsTarget{
			LogQueryURL: "https://logs.example.com/loki/self-monitoring",
		},
		nil,
		routerTrimLogQueryPrefix,
	)
	g.Expect(err).To(gomega.Succeed())

	r, _ := http.NewRequest(
		http.MethodPost,
		"http://localhost:8080/log/query/loki/api/v1/query_range?cluster_id=cluster-1&query="+
			"%7Bcontainer%3D%22tidb%22%7D",
		strings.NewReader("query=%7Bcontainer%3D%22tikv%22%7D&start=1&end=2"),
	)
	r.URL.RawPath = "/log/query/loki/api/v1/query_range"
	modifiedRequest := modifyFunc(r)
	body, err := io.ReadAll(modifiedRequest.Body)

	g.Expect(err).To(gomega.Succeed())
	g.Expect(string(body)).To(gomega.Equal("query=%7Bcontainer%3D%22tikv%22%7D&start=1&end=2"))
	g.Expect(modifiedRequest.URL.Query().Get("cluster_id")).To(gomega.Equal(""))
	g.Expect(modifiedRequest.URL.Query().Get("query")).To(gomega.Equal(`{container="tidb"}`))
}

func TestResolveLogQueryOrgFromClusterUsesClinicOrgID(t *testing.T) {
	g := gomega.NewWithT(t)

	oldGetAnyClusterWithOrg := getAnyClusterWithOrg
	defer func() {
		getAnyClusterWithOrg = oldGetAnyClusterWithOrg
	}()
	getAnyClusterWithOrg = func(id string) (*db.Cluster, error) {
		g.Expect(id).To(gomega.Equal("cluster-1"))
		return &db.Cluster{
			OrgID:       101,
			ClusterType: "cloud",
			Organization: db.Organization{
				DbaasOrgID: 999,
				OrgType:    "cloud",
			},
		}, nil
	}

	orgType, oid, err := resolveLogQueryOrgFromCluster(context.Background(), "cluster-1")

	g.Expect(err).To(gomega.Succeed())
	g.Expect(orgType).To(gomega.Equal("cloud"))
	g.Expect(oid).To(gomega.Equal(uint(101)))
}
