package cmd

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

	"github.com/tidbcloud/clinic-cli/internal/config"
)

func TestClusterSearchIDUsesEnvClusterIDWhenFlagOmitted(t *testing.T) {
	t.Setenv("CLINIC_CLUSTER_ID", "cluster-from-env")

	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"items":[{"clusterID":"cluster-from-env"}]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID: "cluster-from-profile",
		},
	}

	if err := clusterCommandHandler(ctx, []string{"search-id"}, &strings.Builder{}); err != nil {
		t.Fatalf("clusterCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "cluster_id", "cluster-from-env")
}

func TestClusterSearchIDUsesProfileClusterIDWhenEnvMissing(t *testing.T) {
	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"items":[{"clusterID":"cluster-from-profile"}]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID: "cluster-from-profile",
		},
	}

	if err := clusterCommandHandler(ctx, []string{"search-id"}, &strings.Builder{}); err != nil {
		t.Fatalf("clusterCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "cluster_id", "cluster-from-profile")
}

func TestDataQueryClusterIDFlagOverridesEnvAndProfile(t *testing.T) {
	t.Setenv("CLINIC_CLUSTER_ID", "cluster-from-env")

	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"rows":[]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID: "cluster-from-profile",
		},
	}

	if err := dataCommandHandler(ctx, []string{
		"query",
		"--cluster-id", "cluster-from-cli",
		"--sql", "select 1",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("dataCommandHandler() error = %v", err)
	}

	payload, ok := client.request.Body.(map[string]any)
	if !ok {
		t.Fatalf("body type = %T, want map[string]any", client.request.Body)
	}
	if got, want := payload["clusterId"], "cluster-from-cli"; got != want {
		t.Fatalf("body.clusterId = %#v, want %#v", got, want)
	}
}

func TestDataQueryUsesProfileClusterIDWhenEnvMissing(t *testing.T) {
	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"rows":[]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID: "cluster-from-profile",
		},
	}

	if err := dataCommandHandler(ctx, []string{
		"query",
		"--sql", "select 1",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("dataCommandHandler() error = %v", err)
	}

	payload, ok := client.request.Body.(map[string]any)
	if !ok {
		t.Fatalf("body type = %T, want map[string]any", client.request.Body)
	}
	if got, want := payload["clusterId"], "cluster-from-profile"; got != want {
		t.Fatalf("body.clusterId = %#v, want %#v", got, want)
	}
}

func TestMetricsRangeUsesEnvRangeStartAndEndWhenFlagsOmitted(t *testing.T) {
	t.Setenv("CLINIC_RANGE_START", "1712123000")
	t.Setenv("CLINIC_RANGE_END", "1712123600")

	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":{"result":[]}}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			RangeStart: "1712121000",
			RangeEnd:   "1712122000",
		},
	}

	if err := metricsCommandHandler(ctx, []string{
		"range",
		"--cluster-id", "cluster-42",
		"--query", "up",
		"--step", "60s",
		"--output", "json",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("metricsCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "start", "1712123000")
	assertQueryValue(t, client.request.Query, "end", "1712123600")
}

func TestMetricsRangeRangeStartEndFlagsOverrideEnvAndProfile(t *testing.T) {
	t.Setenv("CLINIC_RANGE_START", "1712123000")
	t.Setenv("CLINIC_RANGE_END", "1712123600")

	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":{"result":[]}}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			RangeStart: "1712121000",
			RangeEnd:   "1712122000",
		},
	}

	if err := metricsCommandHandler(ctx, []string{
		"range",
		"--cluster-id", "cluster-42",
		"--query", "up",
		"--start", "1712122000",
		"--end", "1712122600",
		"--step", "60s",
		"--output", "json",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("metricsCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "start", "1712122000")
	assertQueryValue(t, client.request.Query, "end", "1712122600")
}

func TestMetricsRangeUsesProfileRangeStartAndEndWhenEnvMissing(t *testing.T) {
	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":{"result":[]}}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			RangeStart: "1712123000",
			RangeEnd:   "1712123600",
		},
	}

	if err := metricsCommandHandler(ctx, []string{
		"range",
		"--cluster-id", "cluster-42",
		"--query", "up",
		"--step", "60s",
		"--output", "json",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("metricsCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "start", "1712123000")
	assertQueryValue(t, client.request.Query, "end", "1712123600")
}

func TestLokiQueryRangeUsesEnvRangeStartAndEndWhenFlagsOmitted(t *testing.T) {
	t.Setenv("CLINIC_RANGE_START", "1712123000")
	t.Setenv("CLINIC_RANGE_END", "1712123600000")

	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":{"resultType":"streams","result":[]}}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID:           "cluster-42",
			OrgID:               "org-1",
			ClusterDeployTypeV2: "dedicated",
			RangeStart:          "1712121000",
			RangeEnd:            "1712122000",
		},
	}

	if err := lokiCommandHandler(ctx, []string{
		"query",
		"--cluster-id", "cluster-42",
		"--output", "json",
		`{app="tidb", cluster_env="staging", namespace="tidbcluster-42"}`,
	}, &strings.Builder{}); err != nil {
		t.Fatalf("lokiCommandHandler() error = %v", err)
	}

	if got, want := client.request.Method, http.MethodGet; got != want {
		t.Fatalf("method = %q, want %q", got, want)
	}
	assertQueryValue(t, client.request.Query, "start", "1712123000000000000")
	assertQueryValue(t, client.request.Query, "end", "1712123600000000000")
}

func TestLokiQueryRangeUsesProfileRangeStartAndEndWhenEnvMissing(t *testing.T) {
	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":{"resultType":"streams","result":[]}}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID:           "cluster-42",
			OrgID:               "org-1",
			ClusterDeployTypeV2: "dedicated",
			RangeStart:          "1712123000",
			RangeEnd:            "1712123600000",
		},
	}

	if err := lokiCommandHandler(ctx, []string{
		"query",
		"--cluster-id", "cluster-42",
		"--output", "json",
		`{app="tidb", cluster_env="staging", namespace="tidbcluster-42"}`,
	}, &strings.Builder{}); err != nil {
		t.Fatalf("lokiCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "start", "1712123000000000000")
	assertQueryValue(t, client.request.Query, "end", "1712123600000000000")
}

func TestMetricsSeriesUsesProfileRangeStartAndEndWhenEnvMissing(t *testing.T) {
	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":[]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			RangeStart: "1712123000",
			RangeEnd:   "1712123600",
		},
	}

	if err := metricsCommandHandler(ctx, []string{
		"series",
		"--cluster-id", "cluster-42",
		"--match", `{__name__="up"}`,
	}, &strings.Builder{}); err != nil {
		t.Fatalf("metricsCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "start", "1712123000")
	assertQueryValue(t, client.request.Query, "end", "1712123600")
}

func TestLokiLabelsUsesProfileRangeStartAndEndWhenEnvMissing(t *testing.T) {
	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":["cluster"]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			RangeStart: "1712123000",
			RangeEnd:   "1712123600000",
		},
	}

	if err := lokiCommandHandler(ctx, []string{
		"labels",
		"--cluster-id", "cluster-42",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("lokiCommandHandler() error = %v", err)
	}

	if got, want := client.request.Method, http.MethodGet; got != want {
		t.Fatalf("method = %q, want %q", got, want)
	}
	assertQueryValue(t, client.request.Query, "start", "1712123000000000000")
	assertQueryValue(t, client.request.Query, "end", "1712123600000000000")
}

func TestLokiLabelValuesUsesEnvRangeBeforeProfile(t *testing.T) {
	t.Setenv("CLINIC_RANGE_START", "1712123000")
	t.Setenv("CLINIC_RANGE_END", "1712123600000")

	client := &recordingClinicClient{responses: []clinicResponse{{body: []byte(`{"status":"success","data":["tidb"]}`)}}}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			RangeStart: "1712121000",
			RangeEnd:   "1712122000000",
		},
	}

	if err := lokiCommandHandler(ctx, []string{
		"label-values",
		"--cluster-id", "cluster-42",
		"--label", "component",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("lokiCommandHandler() error = %v", err)
	}

	assertQueryValue(t, client.request.Query, "start", "1712123000000000000")
	assertQueryValue(t, client.request.Query, "end", "1712123600000000000")
}

func TestEventsListUsesEnvClusterIDWhenFlagOmitted(t *testing.T) {
	t.Setenv("CLINIC_CLUSTER_ID", "cluster-from-env")

	client := &recordingClinicClient{
		responses: []clinicResponse{
			{body: []byte(`{"items":[{"clusterID":"cluster-from-env","orgID":"org-42"}]}`)},
			{body: []byte(`{"items":[{"id":"evt-1"}]}`)},
		},
	}
	ctx := &RuntimeContext{
		Client: client,
		SelectedProfile: &config.ProfileRecord{
			ClusterID:  "cluster-from-profile",
			RangeStart: "1712122000",
			RangeEnd:   "1712122600",
		},
	}

	if err := eventsCommandHandler(ctx, []string{
		"list",
		"--start", "1712123000",
		"--end", "1712123600",
	}, &strings.Builder{}); err != nil {
		t.Fatalf("eventsCommandHandler() error = %v", err)
	}

	if got, want := len(client.requests), 2; got != want {
		t.Fatalf("request count = %d, want %d", got, want)
	}
	assertQueryValue(t, client.requests[0].Query, "cluster_id", "cluster-from-env")
	if got, want := client.requests[1].Path, "/clinic/api/v1/activityhub/applications/org-42/targets/cluster-from-env/activities"; got != want {
		t.Fatalf("path = %q, want %q", got, want)
	}
}

func TestClusterSearchIDIgnoresLegacyProfileDefaultsWhenNoSelectedProfile(t *testing.T) {
	ctx := &RuntimeContext{
		Client: &recordingClinicClient{},
		SelectedProfileDefaults: config.ProfileDefaults{
			ClusterID: "cluster-from-legacy-defaults",
		},
	}

	err := clusterCommandHandler(ctx, []string{"search-id"}, &strings.Builder{})
	if err == nil {
		t.Fatal("clusterCommandHandler() error = nil, want validation error")
	}
	if got, want := err.Error(), "missing required --cluster-id"; got != want {
		t.Fatalf("error = %q, want %q", got, want)
	}
}
