// Package repositoryroutecontract is the dependency-light request contract
// shared by AGS, AgentTeam Gateway, AgentTeam forwarders, and Desktop.
package repositoryroutecontract

import (
	"net/http"
	"strings"
)

// Operation is the stable method-plus-pattern identity of one real AGS
// registration. It is intentionally exact: one operation cannot silently grow
// when a new route is added beneath an existing prefix.
type Operation string

const (
	OperationUnsupported     Operation = ""
	OperationViewerRepos     Operation = "GET /api/v3/user/repos"
	OperationCodeSearch      Operation = "GET /api/v3/search/code"
	OperationLabelSearch     Operation = "GET /api/v3/search/labels"
	OperationGitInfoRefs     Operation = "GET /{owner}/{repo}/info/refs"
	OperationGitUploadPack   Operation = "POST /{owner}/{repo}/git-upload-pack"
	OperationGitReceivePack  Operation = "POST /{owner}/{repo}/git-receive-pack"
	OperationWorkspaceAvatar Operation = "GET /avatars/{login}"
)

// OperationForRegistration returns the exact operation identity owned by an
// AGS method-plus-pattern registration.
func OperationForRegistration(method, pattern string) Operation {
	method = strings.ToUpper(strings.TrimSpace(method))
	pattern = strings.TrimSpace(pattern)
	if method == "" || pattern == "" {
		return OperationUnsupported
	}
	return Operation(method + " " + pattern)
}

// AuthenticationScope is the middleware authentication contract on the real
// AGS registration. It is independent of AgentTeam split deployment authority.
type AuthenticationScope string

const (
	AuthenticationUnsupported   AuthenticationScope = ""
	AuthenticationPublic        AuthenticationScope = "public"
	AuthenticationOptional      AuthenticationScope = "optional_auth"
	AuthenticationAuthenticated AuthenticationScope = "authenticated"
)

// AuthorityMode is the AgentTeam split authority selected by the activation
// overlay. It is distinct from the AGS registration's AuthenticationScope.
type AuthorityMode string

const (
	AuthorityUnsupported                   AuthorityMode = ""
	AuthoritySessionWorkspace              AuthorityMode = "session_workspace"
	AuthorityRepositoryCredentialOrSession AuthorityMode = "repository_credential_or_session"
	AuthorityGitCredentialOnly             AuthorityMode = "git_credential_only"
	AuthorityPublicWorkspaceDiscovery      AuthorityMode = "public_workspace_discovery"
)

type ScopeKind int

const (
	ScopeUnsupported ScopeKind = iota
	ScopeSessionWorkspace
	ScopeRepositoryCredentialOrSession
	ScopeGitCredentialOnly
	ScopePublicWorkspaceDiscovery
)

type ScopeSource string

const (
	ScopeSourceAGSRegistered      ScopeSource = "ags_registered"
	ScopeSourceAgentTeamSynthetic ScopeSource = "agentteam_synthetic"
)

type Scope struct {
	RegisteredScope ScopeKind
	EdgePolicy      ScopeKind
	Source          ScopeSource
	Repository      string
	Capability      string
	Raw             *RawRequest
}

// ScopeFromRegistration converts AGS registration-owned metadata into AgentTeam's
// request scope without re-parsing a concrete AGS path. Incompatible
// registration authentication and split authority contracts fail closed.
func ScopeFromRegistration(operation Operation, authentication AuthenticationScope, authority AuthorityMode, repository, capability string) Scope {
	kind := ScopeUnsupported
	switch authority {
	case AuthoritySessionWorkspace:
		if authentication == AuthenticationAuthenticated {
			kind = ScopeSessionWorkspace
		}
	case AuthorityRepositoryCredentialOrSession:
		if authentication == AuthenticationOptional || authentication == AuthenticationAuthenticated {
			kind = ScopeRepositoryCredentialOrSession
		}
	case AuthorityGitCredentialOnly:
		if authentication == AuthenticationOptional {
			kind = ScopeGitCredentialOnly
		}
	case AuthorityPublicWorkspaceDiscovery:
		if authentication == AuthenticationPublic {
			kind = ScopePublicWorkspaceDiscovery
		}
	}
	if operation == OperationUnsupported {
		kind = ScopeUnsupported
	}
	if (kind == ScopeRepositoryCredentialOrSession || kind == ScopeGitCredentialOnly) &&
		(repository == "" || capability == "") {
		kind = ScopeUnsupported
	}
	return Scope{
		RegisteredScope: kind,
		EdgePolicy:      kind,
		Source:          ScopeSourceAGSRegistered,
		Repository:      repository,
		Capability:      capability,
	}
}

// Classify is retained for Desktop/runtime candidate preclassification and
// AgentTeam-owned synthetic raw. It is not split activation authority; Gateway and
// Workspace seams match real AGS registrations before consulting their overlay.
func Classify(request *http.Request) Scope {
	if synthetic, ok := ClassifySynthetic(request); ok {
		return synthetic
	}
	registered := ClassifyAGSRequest(request)
	registered.Source = ScopeSourceAGSRegistered
	registered.EdgePolicy = registered.RegisteredScope
	if _, _, ok := RepositoryIDAuthorizationTarget(request); ok {
		registered.EdgePolicy = ScopeUnsupported
	}
	return registered
}

// ClassifySynthetic recognizes only AgentTeam-owned routes that have no AGS
// registration. Registered AGS operations must use registration metadata.
func ClassifySynthetic(request *http.Request) (Scope, bool) {
	raw, err := ParseRawRequest(request)
	if err != nil {
		return Scope{}, false
	}
	return Scope{
		RegisteredScope: ScopeRepositoryCredentialOrSession,
		EdgePolicy:      ScopeRepositoryCredentialOrSession,
		Source:          ScopeSourceAgentTeamSynthetic,
		Repository:      raw.Owner + "/" + raw.Repository,
		Capability:      raw.Capability,
		Raw:             &raw,
	}, true
}
