package rbac

import (
	"bytes"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
)

func TestParseTidbIdFromPath(t *testing.T) {
	testCase := map[string]string{
		"/tidbs/10411292523848492340/publicConnectionSetting": "10411292523848492340",
	}

	for path, id := range testCase {
		tidbId, err := parseTidbIdFromPath(path)
		if err != nil {
			t.Errorf("parse tidb id from path failed: %v", err)
		}
		if tidbId != id {
			t.Errorf("parse tidb id from path failed: %v", tidbId)
		}
	}
}

func TestGetProjectIdFromBody(t *testing.T) {
	tests := []struct {
		name string
		body string
		want string
	}{
		{
			name: "valid project id in root labels",
			body: `{"labels": {"tidb.cloud/project": "123"}}`,
			want: "123",
		},
		{
			name: "valid project id in nested labels",
			body: `{"tidb": {"labels": {"tidb.cloud/project": "123"}}}`,
			want: "123",
		},
		{
			name: "root labels without project label",
			body: `{"labels": {"foo": "bar"}}`,
			want: "",
		},
		{
			name: "nested labels without project label",
			body: `{"tidb": {"labels": {"foo": "bar"}}}`,
			want: "",
		},
		{
			name: "no labels",
			body: `{"tidb": {"name": "test"}}`,
			want: "",
		},
		{
			name: "no project id",
			body: `{"tidb": {"name": "test"}}`,
			want: "",
		},
		{
			name: "invalid json",
			body: `invalid`,
			want: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
			ctx.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))

			got, err := getProjectIdFromBody(ctx)
			if err != nil {
				t.Errorf("getProjectIdFromBody() error = %v", err)
				return
			}
			if got != tt.want {
				t.Errorf("getProjectIdFromBody() = %v, want %v", got, tt.want)
			}

			// Verify body is restored
			restored, _ := io.ReadAll(ctx.Request.Body)
			if string(restored) != tt.body {
				t.Errorf("Body not restored correctly")
			}
		})
	}
}

func TestResolveNextgenResourceAndProjectID(t *testing.T) {
	tests := []struct {
		name           string
		resource       string
		body           string
		wantResource   string
		wantProjectID  string
		expectBodySame bool
	}{
		{
			name:           "create tidb switches to project scoped resource",
			resource:       "/nextgen/orgs/{org_id}/tidbs/create",
			body:           `{"tidb": {"labels": {"tidb.cloud/project": "123"}}}`,
			wantResource:   "/nextgen/orgs/{org_id}/projects/{project_id}/tidbs/create",
			wantProjectID:  "123",
			expectBodySame: true,
		},
		{
			name:           "restore tidb switches to project scoped resource",
			resource:       "/nextgen/orgs/{org_id}/tidbs/restore",
			body:           `{"tidb": {"labels": {"tidb.cloud/project": "456"}}}`,
			wantResource:   "/nextgen/orgs/{org_id}/projects/{project_id}/tidbs/restore",
			wantProjectID:  "456",
			expectBodySame: true,
		},
		{
			name:           "resource remains org scoped when project id is absent",
			resource:       "/nextgen/orgs/{org_id}/tidbs/restore",
			body:           `{"tidb": {"displayName": "restore-target"}}`,
			wantResource:   "/nextgen/orgs/{org_id}/tidbs/restore",
			wantProjectID:  "",
			expectBodySame: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
			ctx.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))

			gotResource, gotProjectID, err := resolveNextgenResourceAndProjectID(ctx, tt.resource)
			if err != nil {
				t.Fatalf("resolveNextgenResourceAndProjectID() error = %v", err)
			}
			if gotResource != tt.wantResource {
				t.Fatalf("resolveNextgenResourceAndProjectID() resource = %v, want %v", gotResource, tt.wantResource)
			}
			if gotProjectID != tt.wantProjectID {
				t.Fatalf("resolveNextgenResourceAndProjectID() projectID = %v, want %v", gotProjectID, tt.wantProjectID)
			}

			if tt.expectBodySame {
				restored, _ := io.ReadAll(ctx.Request.Body)
				if string(restored) != tt.body {
					t.Errorf("Body not restored correctly")
				}
			}
		})
	}
}
