package auth

import (
	"testing"
)

func TestScopeIsValid(t *testing.T) {
	tests := []struct {
		name  string
		scope Scope
		want  bool
	}{
		{"projects:read", ScopeProjectsRead, true},
		{"projects:execute", ScopeProjectsExecute, true},
		{"keys:read", ScopeKeysRead, true},
		{"keys:write", ScopeKeysWrite, true},
		{"admin", ScopeAdmin, true},
		{"invalid", Scope("invalid"), false},
		{"empty", Scope(""), false},
		{"similar", Scope("projects:write"), false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.scope.IsValid(); got != tt.want {
				t.Errorf("Scope(%q).IsValid() = %v, want %v", tt.scope, got, tt.want)
			}
		})
	}
}

func TestScopesFromStrings(t *testing.T) {
	input := []string{"projects:read", "keys:write"}
	got := ScopesFromStrings(input)

	if len(got) != 2 {
		t.Fatalf("ScopesFromStrings() returned %d scopes, want 2", len(got))
	}

	if got[0] != ScopeProjectsRead {
		t.Errorf("got[0] = %v, want %v", got[0], ScopeProjectsRead)
	}

	if got[1] != ScopeKeysWrite {
		t.Errorf("got[1] = %v, want %v", got[1], ScopeKeysWrite)
	}
}

func TestScopesToStrings(t *testing.T) {
	input := []Scope{ScopeProjectsRead, ScopeKeysWrite}
	got := ScopesToStrings(input)

	if len(got) != 2 {
		t.Fatalf("ScopesToStrings() returned %d strings, want 2", len(got))
	}

	if got[0] != "projects:read" {
		t.Errorf("got[0] = %q, want %q", got[0], "projects:read")
	}

	if got[1] != "keys:write" {
		t.Errorf("got[1] = %q, want %q", got[1], "keys:write")
	}
}

func TestValidateScopes(t *testing.T) {
	tests := []struct {
		name   string
		scopes []Scope
		want   bool
	}{
		{"all valid", []Scope{ScopeProjectsRead, ScopeKeysWrite}, true},
		{"single valid", []Scope{ScopeAdmin}, true},
		{"empty", []Scope{}, true},
		{"one invalid", []Scope{ScopeProjectsRead, Scope("invalid")}, false},
		{"all invalid", []Scope{Scope("foo"), Scope("bar")}, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := ValidateScopes(tt.scopes); got != tt.want {
				t.Errorf("ValidateScopes() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestHasScope(t *testing.T) {
	tests := []struct {
		name     string
		scopes   []Scope
		required Scope
		want     bool
	}{
		{"has exact scope", []Scope{ScopeProjectsRead, ScopeKeysRead}, ScopeProjectsRead, true},
		{"admin grants all", []Scope{ScopeAdmin}, ScopeProjectsRead, true},
		{"admin grants keys", []Scope{ScopeAdmin}, ScopeKeysWrite, true},
		{"missing scope", []Scope{ScopeProjectsRead}, ScopeKeysWrite, false},
		{"empty scopes", []Scope{}, ScopeProjectsRead, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := HasScope(tt.scopes, tt.required); got != tt.want {
				t.Errorf("HasScope() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestHasAnyScope(t *testing.T) {
	tests := []struct {
		name     string
		scopes   []Scope
		required []Scope
		want     bool
	}{
		{"has first", []Scope{ScopeProjectsRead}, []Scope{ScopeProjectsRead, ScopeKeysRead}, true},
		{"has second", []Scope{ScopeKeysRead}, []Scope{ScopeProjectsRead, ScopeKeysRead}, true},
		{"has neither", []Scope{ScopeKeysWrite}, []Scope{ScopeProjectsRead, ScopeKeysRead}, false},
		{"admin grants any", []Scope{ScopeAdmin}, []Scope{ScopeProjectsRead, ScopeKeysRead}, true},
		{"empty required", []Scope{ScopeProjectsRead}, []Scope{}, false},
		{"empty scopes", []Scope{}, []Scope{ScopeProjectsRead}, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := HasAnyScope(tt.scopes, tt.required...); got != tt.want {
				t.Errorf("HasAnyScope() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestHasProjectAccess(t *testing.T) {
	tests := []struct {
		name     string
		allowed  []string
		project  string
		want     bool
	}{
		{"nil allows all", nil, "any-project", true},
		{"in list", []string{"proj-a", "proj-b"}, "proj-a", true},
		{"not in list", []string{"proj-a", "proj-b"}, "proj-c", false},
		{"empty list denies", []string{}, "proj-a", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := HasProjectAccess(tt.allowed, tt.project); got != tt.want {
				t.Errorf("HasProjectAccess() = %v, want %v", got, tt.want)
			}
		})
	}
}
