package apikey

import (
	"testing"

	"github.com/tidbcloud/account/internel/dao/entity"
)

func TestOrganizationIDFromRoleBindings(t *testing.T) {
	tests := []struct {
		name     string
		bindings []entity.RoleBinding
		want     uint64
		wantErr  bool
	}{
		{
			name: "organization scoped API key",
			bindings: []entity.RoleBinding{{
				ScopeType: entity.RoleBindingScopeOrg,
				ScopeID:   60205,
				Status:    entity.RoleBindingStatusActive,
			}},
			want: 60205,
		},
		{
			name: "project scoped API key keeps its organization membership",
			bindings: []entity.RoleBinding{
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60205, Status: entity.RoleBindingStatusActive},
				{ScopeType: entity.RoleBindingScopeProject, ScopeID: 9001, Status: entity.RoleBindingStatusActive},
			},
			want: 60205,
		},
		{
			name: "inactive organization binding is ignored",
			bindings: []entity.RoleBinding{
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60205, Status: entity.RoleBindingStatusInactive},
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60206, Status: entity.RoleBindingStatusActive},
			},
			want: 60206,
		},
		{
			name:     "missing organization binding",
			bindings: []entity.RoleBinding{{ScopeType: entity.RoleBindingScopeProject, ScopeID: 9001, Status: entity.RoleBindingStatusActive}},
			wantErr:  true,
		},
		{
			name: "ambiguous organization bindings",
			bindings: []entity.RoleBinding{
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60205, Status: entity.RoleBindingStatusActive},
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60206, Status: entity.RoleBindingStatusActive},
			},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := organizationIDFromRoleBindings(tt.bindings)
			if (err != nil) != tt.wantErr {
				t.Fatalf("organizationIDFromRoleBindings() error = %v, wantErr %v", err, tt.wantErr)
			}
			if got != tt.want {
				t.Fatalf("organizationIDFromRoleBindings() = %d, want %d", got, tt.want)
			}
		})
	}
}

func TestAuthnResourceInfosFromRoleBindingsReturnsActiveScopes(t *testing.T) {
	bindings := []entity.RoleBinding{
		{
			ScopeType: entity.RoleBindingScopeOrg,
			ScopeID:   60205,
			Role:      entity.RoleOrgMember,
			RoleID:    30,
			Status:    entity.RoleBindingStatusActive,
		},
		{
			ScopeType: entity.RoleBindingScopeProject,
			ScopeID:   103547,
			Role:      entity.RoleProjectOwner,
			RoleID:    34,
			Status:    entity.RoleBindingStatusActive,
		},
		{
			ScopeType: entity.RoleBindingScopeProject,
			ScopeID:   103548,
			Role:      entity.RoleProjectReadOnly,
			RoleID:    35,
			Status:    entity.RoleBindingStatusActive,
		},
		{
			ScopeType: entity.RoleBindingScopeProject,
			ScopeID:   999999,
			Role:      entity.RoleProjectDev,
			RoleID:    36,
			Status:    entity.RoleBindingStatusInactive,
		},
	}

	got, err := authnResourceInfosFromRoleBindings(bindings)
	if err != nil {
		t.Fatalf("authnResourceInfosFromRoleBindings() error = %v", err)
	}
	if len(got) != 3 {
		t.Fatalf("resource info count = %d, want 3", len(got))
	}
	if got[0].ScopeType != string(entity.RoleBindingScopeOrg) || got[0].ResourceID != 60205 || got[0].Role != entity.RoleOrgMember || got[0].RoleID != 30 {
		t.Fatalf("org resource info = %#v", got[0])
	}
	if got[1].ScopeType != string(entity.RoleBindingScopeProject) || got[1].ResourceID != 103547 || got[1].Role != entity.RoleProjectOwner || got[1].RoleID != 34 {
		t.Fatalf("first project resource info = %#v", got[1])
	}
	if got[2].ScopeType != string(entity.RoleBindingScopeProject) || got[2].ResourceID != 103548 || got[2].Role != entity.RoleProjectReadOnly || got[2].RoleID != 35 {
		t.Fatalf("second project resource info = %#v", got[2])
	}
}

func TestAuthnResourceInfosFromRoleBindingsRequiresOneOrganization(t *testing.T) {
	tests := []struct {
		name     string
		bindings []entity.RoleBinding
	}{
		{
			name: "missing organization",
			bindings: []entity.RoleBinding{{
				ScopeType: entity.RoleBindingScopeProject,
				ScopeID:   103547,
				Role:      entity.RoleProjectOwner,
				Status:    entity.RoleBindingStatusActive,
			}},
		},
		{
			name: "ambiguous organizations",
			bindings: []entity.RoleBinding{
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60205, Status: entity.RoleBindingStatusActive},
				{ScopeType: entity.RoleBindingScopeOrg, ScopeID: 60206, Status: entity.RoleBindingStatusActive},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if _, err := authnResourceInfosFromRoleBindings(tt.bindings); err == nil {
				t.Fatal("authnResourceInfosFromRoleBindings() error = nil, want error")
			}
		})
	}
}
