package repositoryroutecontract

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestClassifyPreservesRegisteredAndSyntheticSource(t *testing.T) {
	t.Parallel()
	registered := Classify(httptest.NewRequest(http.MethodGet, "/api/v3/repos/acme/repo/branches", nil))
	if registered.Source != ScopeSourceAGSRegistered || registered.RegisteredScope != ScopeRepositoryCredentialOrSession || registered.EdgePolicy != ScopeRepositoryCredentialOrSession || registered.Raw != nil {
		t.Fatalf("registered scope=%+v", registered)
	}
	synthetic := Classify(httptest.NewRequest(http.MethodGet, "/acme/repo/raw/main/README.md", nil))
	if synthetic.Source != ScopeSourceAgentTeamSynthetic || synthetic.RegisteredScope != ScopeRepositoryCredentialOrSession || synthetic.EdgePolicy != ScopeRepositoryCredentialOrSession || synthetic.Repository != "acme/repo" || synthetic.Raw == nil {
		t.Fatalf("synthetic scope=%+v", synthetic)
	}
}

func TestClassifySeparatesRegisteredScopeFromSplitEdgePolicy(t *testing.T) {
	t.Parallel()
	for _, test := range []struct {
		method, path string
		registered   ScopeKind
		edge         ScopeKind
	}{
		{http.MethodGet, "/api/v3/repositories/42/environments", ScopeRepositoryCredentialOrSession, ScopeUnsupported},
	} {
		got := Classify(httptest.NewRequest(test.method, test.path, nil))
		if got.RegisteredScope != test.registered || got.EdgePolicy != test.edge {
			t.Fatalf("Classify(%s %s)=%+v", test.method, test.path, got)
		}
	}
}

func TestActivatedRegistrationOverlayUsesOperationAuthorityKeys(t *testing.T) {
	t.Parallel()
	if got := OperationForRegistration(" get ", " /api/v3/user/repos "); got != OperationViewerRepos {
		t.Fatalf("OperationForRegistration()=%q, want %q", got, OperationViewerRepos)
	}
	for _, test := range []struct {
		operation Operation
		authority AuthorityMode
		want      bool
	}{
		{OperationViewerRepos, AuthoritySessionWorkspace, true},
		{OperationViewerRepos, AuthorityRepositoryCredentialOrSession, false},
		{Operation("GET /api/v3/repos/{owner}/{repo}/contents/*"), AuthorityRepositoryCredentialOrSession, true},
		{Operation("PUT /api/v3/repos/{owner}/{repo}/contents/*"), AuthorityRepositoryCredentialOrSession, true},
		{Operation("GET /api/v3/repos/{owner}/{repo}/contents/*"), AuthoritySessionWorkspace, false},
		{OperationGitInfoRefs, AuthorityGitCredentialOnly, true},
		{OperationWorkspaceAvatar, AuthorityPublicWorkspaceDiscovery, true},
		{OperationUnsupported, AuthorityUnsupported, false},
	} {
		if got := IsActivatedOperation(test.operation, test.authority); got != test.want {
			t.Fatalf("IsActivatedOperation(%q, %q)=%v, want %v", test.operation, test.authority, got, test.want)
		}
	}
}

func TestScopeFromRegistrationRequiresCompatibleAuthentication(t *testing.T) {
	t.Parallel()
	for _, test := range []struct {
		name           string
		authentication AuthenticationScope
		authority      AuthorityMode
		repository     string
		capability     string
		want           ScopeKind
	}{
		{"session", AuthenticationAuthenticated, AuthoritySessionWorkspace, "", "", ScopeSessionWorkspace},
		{"session rejects optional auth", AuthenticationOptional, AuthoritySessionWorkspace, "", "", ScopeUnsupported},
		{"repository optional auth", AuthenticationOptional, AuthorityRepositoryCredentialOrSession, "acme/docs", "repo:read", ScopeRepositoryCredentialOrSession},
		{"repository authenticated", AuthenticationAuthenticated, AuthorityRepositoryCredentialOrSession, "acme/docs", "repo:read", ScopeRepositoryCredentialOrSession},
		{"repository requires capability", AuthenticationOptional, AuthorityRepositoryCredentialOrSession, "acme/docs", "", ScopeUnsupported},
		{"git", AuthenticationOptional, AuthorityGitCredentialOnly, "acme/docs", "repo:clone", ScopeGitCredentialOnly},
		{"git rejects authenticated-only", AuthenticationAuthenticated, AuthorityGitCredentialOnly, "acme/docs", "repo:clone", ScopeUnsupported},
		{"public", AuthenticationPublic, AuthorityPublicWorkspaceDiscovery, "", "", ScopePublicWorkspaceDiscovery},
		{"public rejects optional auth", AuthenticationOptional, AuthorityPublicWorkspaceDiscovery, "", "", ScopeUnsupported},
	} {
		t.Run(test.name, func(t *testing.T) {
			scope := ScopeFromRegistration(Operation("GET /registered"), test.authentication, test.authority, test.repository, test.capability)
			if scope.EdgePolicy != test.want || scope.RegisteredScope != test.want {
				t.Fatalf("ScopeFromRegistration()=%+v, want %v", scope, test.want)
			}
		})
	}
}

func TestClassifyPublicWorkspaceAvatarIsExact(t *testing.T) {
	t.Parallel()
	for _, test := range []struct {
		method string
		path   string
		want   ScopeKind
	}{
		{http.MethodGet, "/avatars/alice", ScopePublicWorkspaceDiscovery},
		{http.MethodPost, "/avatars/alice", ScopeUnsupported},
		{http.MethodGet, "/avatars", ScopeUnsupported},
		{http.MethodGet, "/avatars/alice/extra", ScopeUnsupported},
		{http.MethodGet, "/avatars/..", ScopeUnsupported},
	} {
		got := ClassifyAGSRequest(httptest.NewRequest(test.method, test.path, nil))
		if got.RegisteredScope != test.want {
			t.Fatalf("ClassifyAGSRequest(%s %s)=%v, want %v", test.method, test.path, got.RegisteredScope, test.want)
		}
	}
}
