package permission

import "testing"

func TestHasRole(t *testing.T) {
	tests := []struct {
		groups []string
		role   Role
		want   bool
	}{
		{[]string{"role:admin"}, RoleAdmin, true},
		{[]string{"admin"}, RoleAdmin, true},
		{[]string{"role:user"}, RoleAdmin, false},
		{[]string{" role:service "}, RoleService, true},
	}

	for _, tt := range tests {
		if got := HasRole(tt.groups, tt.role); got != tt.want {
			t.Fatalf("HasRole(%v, %q) = %v, want %v", tt.groups, tt.role, got, tt.want)
		}
	}
}

func TestHasPermissionHierarchy(t *testing.T) {
	groups := []string{"drive:write"}

	if !HasPermission(groups, ResourceDrive, LevelRead) {
		t.Fatal("write should satisfy read")
	}
	if !HasPermission(groups, ResourceDrive, LevelWrite) {
		t.Fatal("write should satisfy write")
	}
	if HasPermission(groups, ResourceDrive, LevelAdmin) {
		t.Fatal("write should not satisfy admin")
	}
}

func TestHasPermissionAdminBypass(t *testing.T) {
	groups := []string{"role:admin"}

	if !HasPermission(groups, ResourcePhotos, LevelAdmin) {
		t.Fatal("platform admin should bypass resource checks")
	}
}

func TestHasPermissionResourceAdmin(t *testing.T) {
	groups := []string{"calendar:admin"}

	if !HasPermission(groups, ResourceCalendar, LevelRead) {
		t.Fatal("resource admin should satisfy read")
	}
	if !HasPermission(groups, ResourceCalendar, LevelWrite) {
		t.Fatal("resource admin should satisfy write")
	}
	if !HasPermission(groups, ResourceCalendar, LevelAdmin) {
		t.Fatal("resource admin should satisfy admin")
	}
}

func TestWithSuiteDefaultsEmptyGroups(t *testing.T) {
	groups := WithSuiteDefaults(nil)

	if !HasRole(groups, RoleUser) {
		t.Fatal("expected role:user")
	}
	if !HasPermission(groups, ResourceContacts, LevelWrite) {
		t.Fatal("expected contacts write")
	}
	if !HasPermission(groups, ResourceCalendar, LevelWrite) {
		t.Fatal("expected calendar write")
	}
}

func TestWithSuiteDefaultsPreservesExplicitResource(t *testing.T) {
	groups := WithSuiteDefaults([]string{"contacts:read"})

	if !HasPermission(groups, ResourceContacts, LevelRead) {
		t.Fatal("expected contacts read")
	}
	if HasPermission(groups, ResourceContacts, LevelWrite) {
		t.Fatal("contacts:read must not be upgraded to write")
	}
	if HasPermission(groups, ResourceDrive, LevelRead) {
		t.Fatal("must not grant drive when contacts-only group is present")
	}
}

func TestDeriveAccountRole(t *testing.T) {
	if got := DeriveAccountRole(true, "active"); got != AccountRoleAdmin {
		t.Fatalf("admin = %q", got)
	}
	if got := DeriveAccountRole(false, "active"); got != AccountRoleUser {
		t.Fatalf("user = %q", got)
	}
	if got := DeriveAccountRole(false, "invited"); got != AccountRoleGuest {
		t.Fatalf("guest = %q", got)
	}
	if got := DeriveAccountRole(true, "disabled"); got != AccountRoleSuspended {
		t.Fatalf("suspended = %q", got)
	}
}

func TestWithGuestAccess(t *testing.T) {
	groups := WithGuestAccess(nil)
	if !HasRole(groups, RoleGuest) {
		t.Fatal("expected guest role")
	}
	if !HasPermission(groups, ResourceDrive, LevelWrite) {
		t.Fatal("expected drive write for own uploads")
	}
	if HasPermission(groups, ResourceContacts, LevelRead) {
		t.Fatal("guest must not get contacts")
	}
	if HasPermission(groups, ResourcePhotos, LevelRead) {
		t.Fatal("guest must not get photos")
	}
}

func TestWithSuiteDefaultsUserRoleOnly(t *testing.T) {
	groups := WithSuiteDefaults([]string{"role:user"})

	if !HasPermission(groups, ResourceContacts, LevelWrite) {
		t.Fatal("role:user without resource groups should get suite defaults")
	}
}

func TestHasPermissionIsolation(t *testing.T) {
	groups := []string{"contacts:read"}

	if !HasPermission(groups, ResourceContacts, LevelRead) {
		t.Fatal("expected contacts read")
	}
	if HasPermission(groups, ResourceDrive, LevelRead) {
		t.Fatal("contacts permission must not grant drive access")
	}
}

func TestAdminScopeString(t *testing.T) {
	tests := []struct {
		scope AdminScope
		want  string
	}{
		{AdminScopeRead, "read"},
		{AdminScopeWrite, "write"},
		{AdminScope(0), "unknown"},
	}

	for _, tt := range tests {
		if got := tt.scope.String(); got != tt.want {
			t.Fatalf("AdminScope(%d).String() = %q, want %q", tt.scope, got, tt.want)
		}
	}
}

func TestParseAdminScope(t *testing.T) {
	tests := []struct {
		in   string
		want AdminScope
		ok   bool
	}{
		{"read", AdminScopeRead, true},
		{"write", AdminScopeWrite, true},
		{" READ ", AdminScopeRead, true},
		{"Write", AdminScopeWrite, true},
		{"admin", 0, false},
		{"", 0, false},
	}

	for _, tt := range tests {
		got, ok := ParseAdminScope(tt.in)
		if ok != tt.ok || got != tt.want {
			t.Fatalf("ParseAdminScope(%q) = (%v, %v), want (%v, %v)", tt.in, got, ok, tt.want, tt.ok)
		}
	}
}

func TestHasAdminScopeReadOnly(t *testing.T) {
	groups := []string{GroupAdminRead}

	if !HasAdminScope(groups, AdminScopeRead) {
		t.Fatal("admin:read should satisfy read scope")
	}
	if HasAdminScope(groups, AdminScopeWrite) {
		t.Fatal("admin:read should not satisfy write scope")
	}
}

func TestHasAdminScopeWriteImpliesRead(t *testing.T) {
	groups := []string{GroupAdminWrite}

	if !HasAdminScope(groups, AdminScopeRead) {
		t.Fatal("admin:write should satisfy read scope")
	}
	if !HasAdminScope(groups, AdminScopeWrite) {
		t.Fatal("admin:write should satisfy write scope")
	}
}

func TestHasAdminScopePlatformAdminBypass(t *testing.T) {
	tests := []struct {
		name   string
		groups []string
	}{
		{"bare admin role", []string{"admin"}},
		{"prefixed admin role", []string{"role:admin"}},
		{"trimmed prefixed admin role", []string{" role:admin "}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if !HasAdminScope(tt.groups, AdminScopeRead) {
				t.Fatal("platform admin should satisfy read scope")
			}
			if !HasAdminScope(tt.groups, AdminScopeWrite) {
				t.Fatal("platform admin should satisfy write scope")
			}
			if !HasAdminScope(tt.groups, DefaultAdminScope) {
				t.Fatal("platform admin should satisfy default admin scope")
			}
		})
	}
}

func TestWithPlatformAdmin(t *testing.T) {
	groups := WithSuiteDefaults(nil)
	out := WithPlatformAdmin(groups)
	if !HasRole(out, RoleAdmin) {
		t.Fatal("expected admin role")
	}
	if !HasAdminScope(out, AdminScopeWrite) {
		t.Fatal("expected admin:write scope")
	}
}

func TestHasAdminScopeNoAccess(t *testing.T) {
	tests := []struct {
		name   string
		groups []string
	}{
		{"empty groups", nil},
		{"unrelated user role", []string{"role:user"}},
		{"resource permission only", []string{"drive:admin"}},
		{"unknown admin suffix", []string{"admin:unknown"}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if HasAdminScope(tt.groups, AdminScopeRead) {
				t.Fatal("expected no admin read scope")
			}
			if HasAdminScope(tt.groups, AdminScopeWrite) {
				t.Fatal("expected no admin write scope")
			}
		})
	}
}

func TestHasAdminScopeMaxWins(t *testing.T) {
	groups := []string{GroupAdminRead, GroupAdminWrite}

	if !HasAdminScope(groups, AdminScopeRead) {
		t.Fatal("combined scopes should satisfy read")
	}
	if !HasAdminScope(groups, AdminScopeWrite) {
		t.Fatal("combined scopes should satisfy write")
	}
}

func TestHasAdminScopeNormalization(t *testing.T) {
	groups := []string{" Admin:Write "}

	if !HasAdminScope(groups, AdminScopeWrite) {
		t.Fatal("admin scope groups should be case- and whitespace-normalized")
	}
	if !HasAdminScope(groups, AdminScopeRead) {
		t.Fatal("normalized admin:write should still imply read")
	}
}
