package system

// classify() turns a request path into the (module, action) pair the activity
// log stores. The second segment is the tricky one: a non-id there is usually
// a sub-resource (inventory/stock-in), but sometimes it is the verb itself
// (auth/logout). Getting it wrong does not break anything — it just makes the
// log read nonsense — which is why the cases below assert rendered meaning,
// not just string shapes.

import "testing"

func TestClassifyPathAction(t *testing.T) {
	cases := []struct {
		name         string
		path, method string
		wantModule   string
		wantAction   string
		wantModelID  *int64
	}{
		{"confirm under id", "/api/v1/sales-orders/12/confirm", "POST", "sales-orders", "post/confirm", int64ptr(12)},
		{"toggle under id", "/api/v1/users/68/toggle", "POST", "users", "post/toggle", int64ptr(68)},
		{"status under id", "/api/v1/sales-orders/7/status", "PATCH", "sales-orders", "patch/status", int64ptr(7)},
		{"plain post", "/api/v1/pos", "POST", "pos", "post", nil},
		{"put under sub-resource", "/api/v1/memberships/tiers/7", "PUT", "memberships/tiers", "put", int64ptr(7)},
		// The logout case that motivated isActionVerb: "logout" is the verb,
		// not a sub-resource of auth.
		{"logout is verb", "/api/v1/auth/logout", "POST", "auth", "post/logout", nil},
		{"sweep is verb", "/api/v1/midtrans/sweep", "POST", "midtrans", "post/sweep", nil},
		// Hyphenated segments stay sub-resources.
		{"stock-in stays module", "/api/v1/inventory/stock-in", "POST", "inventory/stock-in", "post", nil},
		{"unknown stays module", "/api/v1/reports/quarterly", "POST", "reports/quarterly", "post", nil},
		{"deep action parts", "/api/v1/sales-orders/12/approve/level", "POST", "sales-orders", "post/approve/level", int64ptr(12)},
		{"case-insensitive verb", "/api/v1/auth/Logout", "POST", "auth", "post/logout", nil},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			module, action, id := classify(tc.path, tc.method)
			if module != tc.wantModule {
				t.Errorf("module = %q, want %q", module, tc.wantModule)
			}
			if action != tc.wantAction {
				t.Errorf("action = %q, want %q", action, tc.wantAction)
			}
			if (id == nil) != (tc.wantModelID == nil) || (id != nil && *id != *tc.wantModelID) {
				t.Errorf("modelID = %v, want %v", id, tc.wantModelID)
			}
		})
	}
}

func int64ptr(n int64) *int64 { return &n }

func TestCreatedID(t *testing.T) {
	cases := []struct {
		name string
		body string
		want *int64
	}{
		{"envelope with id", `{"data":{"id":21,"email":"ezaak"}}`, int64ptr(21)},
		{"no id", `{"data":{"email":"x"}}`, nil},
		{"null data", `{"data":null}`, nil},
		{"array data", `{"data":[{"id":1}]}`, nil},
		{"not json", `not json`, nil},
		{"empty", ``, nil},
		{"truncated", `{"data":{"id":2`, nil},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := createdID([]byte(tc.body))
			if (got == nil) != (tc.want == nil) || (got != nil && *got != *tc.want) {
				t.Errorf("createdID(%q) = %v, want %v", tc.body, got, tc.want)
			}
		})
	}
}
