package agent

import (
	"context"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/require"
)

func setTestSharedHTTPClient(t *testing.T, client *http.Client) {
	t.Helper()
	prevClient := sharedHttpClient
	sharedHttpClient = client
	t.Cleanup(func() {
		sharedHttpClient = prevClient
	})
}

func TestNGRegionalClientGetImportRoleConfig(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, http.MethodGet, r.Method)
		require.Equal(t, "/api/premium/v1/clusters/42/importRoleConfig", r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		_, err := w.Write([]byte(`{"roleArn":"arn:aws:iam::123456789012:role/import-role","externalId":"42","bucket":"bucket","allowedPrefix":"global-sort/42","cloudStorageUri":"s3://bucket/global-sort"}`))
		require.NoError(t, err)
	}))
	defer server.Close()

	setTestSharedHTTPClient(t, server.Client())

	client := &NGRegionalClient{BaseUrl: server.URL}
	resp, err := client.GetImportRoleConfig(context.Background(), 42)
	require.NoError(t, err)
	require.Equal(t, "arn:aws:iam::123456789012:role/import-role", resp.RoleArn)
	require.Equal(t, "42", resp.ExternalID)
	require.Equal(t, "bucket", resp.Bucket)
	require.Equal(t, "global-sort/42", resp.AllowedPrefix)
	require.Equal(t, "s3://bucket/global-sort", resp.CloudStorageURI)
}

func TestNGRegionalClientGetImportRoleConfigRejectsEmptyRoleARN(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, http.MethodGet, r.Method)
		require.Equal(t, "/api/premium/v1/clusters/42/importRoleConfig", r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		_, err := w.Write([]byte(`{"externalId":"42"}`))
		require.NoError(t, err)
	}))
	defer server.Close()

	setTestSharedHTTPClient(t, server.Client())

	client := &NGRegionalClient{BaseUrl: server.URL}
	_, err := client.GetImportRoleConfig(context.Background(), 42)
	require.EqualError(t, err, "import role ARN is empty for cluster 42")
}

func TestNGRegionalClientGetImportRoleConfigRejectsEmptyExternalID(t *testing.T) {
	tests := map[string]string{
		"missing external ID": `{"roleArn":"arn:aws:iam::123456789012:role/import-role"}`,
		"null external ID":    `{"roleArn":"arn:aws:iam::123456789012:role/import-role","externalId":null}`,
	}
	for name, body := range tests {
		t.Run(name, func(t *testing.T) {
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				require.Equal(t, http.MethodGet, r.Method)
				require.Equal(t, "/api/premium/v1/clusters/42/importRoleConfig", r.URL.Path)
				w.Header().Set("Content-Type", "application/json")
				_, err := w.Write([]byte(body))
				require.NoError(t, err)
			}))
			defer server.Close()

			setTestSharedHTTPClient(t, server.Client())

			client := &NGRegionalClient{BaseUrl: server.URL}
			_, err := client.GetImportRoleConfig(context.Background(), 42)
			require.EqualError(t, err, "import role external ID is empty for cluster 42")
		})
	}
}

func TestNGRegionalClientGetImportRoleConfigRejectsInvalidJSON(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, http.MethodGet, r.Method)
		require.Equal(t, "/api/premium/v1/clusters/42/importRoleConfig", r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		_, err := w.Write([]byte(`{"roleArn":`))
		require.NoError(t, err)
	}))
	defer server.Close()

	setTestSharedHTTPClient(t, server.Client())

	client := &NGRegionalClient{BaseUrl: server.URL}
	_, err := client.GetImportRoleConfig(context.Background(), 42)
	require.Error(t, err)
}

func TestApplyImportRoleConfigToStorageURI(t *testing.T) {
	roleConfig := &ImportRoleConfig{RoleArn: "arn:aws:iam::123456789012:role/import-role", ExternalID: "12345"}

	tests := map[string]struct {
		sourceURI string
		wantRole  bool
	}{
		"s3 appends role auth": {
			sourceURI: "s3://bucket/prefix",
			wantRole:  true,
		},
		"s3 preserves non-auth query params": {
			sourceURI: "s3://bucket/prefix?foo=bar",
			wantRole:  true,
		},
		"s3 replaces old auth params": {
			sourceURI: "s3://bucket/prefix?access-key=AKIA&external-id=99999&role-arn=old&secret-access-key=secret",
			wantRole:  true,
		},
		"oss appends role auth": {
			sourceURI: "oss://bucket/prefix",
			wantRole:  true,
		},
		"azure is unchanged": {
			sourceURI: "azure://container/prefix?account-name=acct",
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			gotURI, err := ApplyImportRoleConfigToStorageURI(tc.sourceURI, roleConfig)
			require.NoError(t, err)

			gotURL, err := url.Parse(gotURI)
			require.NoError(t, err)
			origURL, err := url.Parse(tc.sourceURI)
			require.NoError(t, err)
			require.Equal(t, origURL.Scheme, gotURL.Scheme)

			gotQ := gotURL.Query()
			if tc.wantRole {
				require.Equal(t, roleConfig.RoleArn, gotQ.Get("role-arn"))
				require.Equal(t, roleConfig.ExternalID, gotQ.Get("external-id"))
				require.Empty(t, gotQ.Get("access-key"))
				require.Empty(t, gotQ.Get("secret-access-key"))
			} else {
				require.Empty(t, gotQ.Get("role-arn"))
				require.Empty(t, gotQ.Get("external-id"))
			}

			origQ := origURL.Query()
			for k, v := range origQ {
				if k == "access-key" || k == "external-id" || k == "role-arn" || k == "secret-access-key" {
					continue
				}
				require.Equal(t, v, gotQ[k], "query param %q not preserved", k)
			}
		})
	}
}

func TestApplyImportRoleConfigToStorageURIRejectsEmptyConfig(t *testing.T) {
	_, err := ApplyImportRoleConfigToStorageURI("s3://bucket/prefix", nil)
	require.EqualError(t, err, "import role config is nil")

	_, err = ApplyImportRoleConfigToStorageURI("s3://bucket/prefix", &ImportRoleConfig{ExternalID: "12345"})
	require.EqualError(t, err, "import role ARN is empty")

	_, err = ApplyImportRoleConfigToStorageURI("s3://bucket/prefix", &ImportRoleConfig{RoleArn: "arn"})
	require.NoError(t, err)
}
