package gateway

import (
	"errors"
	"net/http"
	"strings"

	agsserver "github.com/pingcap/agent-git-service/server"

	repositoryroute "github.com/pingcap/agent-git-service/repositoryroutecontract"
	"github.com/tidbcloud/AgentTeam/internal/agsbridge/workspaceurl"
	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/config"
	"github.com/tidbcloud/AgentTeam/internal/core/authidentity"
	"github.com/tidbcloud/AgentTeam/internal/workspace/router"
)

const sessionCandidateAuthority = "session_candidate"

func (handler *Handler) serveWorkspaceAGSSeam(writer http.ResponseWriter, request *http.Request) bool {
	if !workspaceurl.Owns(request) {
		return false
	}
	qualifiedPath, err := workspaceurl.Parse(request)
	if err != nil {
		http.Error(writer, "invalid workspace AGS path", http.StatusBadRequest)
		return true
	}
	nativeRequest := request.Clone(request.Context())
	if err := qualifiedPath.Strip(nativeRequest, qualifiedPath.WorkspaceID); err != nil {
		http.Error(writer, "invalid workspace AGS path", http.StatusBadRequest)
		return true
	}
	scope, activated := activatedAGSScope(nativeRequest)
	if !activated {
		http.Error(writer, "unknown gateway route", http.StatusNotFound)
		return true
	}

	request = request.Clone(request.Context())
	request.Header.Del(api.HeaderRouteWorkspaceID)
	request.Header.Del(api.HeaderWorkspaceID)
	request.Header.Del(router.HeaderResolvedWorkspaceGroupID)
	stripUntrustedAGSIdentityHeaders(request.Header)

	authority := classifyLegacyAGSAuthorization(request.Header.Values("Authorization"))
	switch scope.EdgePolicy {
	case repositoryroute.ScopeSessionWorkspace:
		if authority != legacyAGSAuthoritySession {
			writeAGSAuthorizationError(writer, authority, true)
			return true
		}
		if !handler.resolveWorkspaceQualifiedSessionWorkspace(writer, request, qualifiedPath.WorkspaceID) {
			return true
		}
	case repositoryroute.ScopeRepositoryCredentialOrSession:
		switch authority {
		case legacyAGSAuthorityMissing, legacyAGSAuthorityRepositoryCredential:
			// The canonical path supplies placement only. AGS remains the owner of
			// public-repository and repository-credential authorization.
		case legacyAGSAuthoritySession:
			if !handler.resolveWorkspaceQualifiedSessionWorkspace(writer, request, qualifiedPath.WorkspaceID) {
				return true
			}
		default:
			writeAGSAuthorizationError(writer, authority, false)
			return true
		}
	case repositoryroute.ScopeGitCredentialOnly:
		if authority != legacyAGSAuthorityRepositoryCredential {
			writeAGSAuthorizationError(writer, authority, false)
			return true
		}
	case repositoryroute.ScopePublicWorkspaceDiscovery:
		// The only activated public registration is the stateless avatar
		// redirect. Its workspace-qualified path selects the same owner as the
		// response that emitted it, while the unqualified split route stays 404.
	default:
		http.Error(writer, "unknown gateway route", http.StatusNotFound)
		return true
	}

	request.Header.Set(api.HeaderRouteWorkspaceID, qualifiedPath.WorkspaceID)
	handler.workspace.ServeHTTP(writer, request)
	return true
}

func stripUntrustedAGSIdentityHeaders(header http.Header) {
	header.Del(config.DefaultTrustedJWTHeader)
	header.Del("X-AgentTeam-Acting-User-Verified-JWT")
	header.Del("X-MeshX-Acting-Principal")
}

func activatedAGSScope(request *http.Request) (repositoryroute.Scope, bool) {
	if synthetic, ok := repositoryroute.ClassifySynthetic(request); ok {
		return synthetic, true
	}
	registered, ok := agsserver.MatchRegisteredRoute(request)
	if !ok {
		return repositoryroute.Scope{RegisteredScope: repositoryroute.ScopeUnsupported}, false
	}
	authority, activated := repositoryroute.ActivatedAuthority(registered.Operation)
	if !activated {
		return repositoryroute.Scope{RegisteredScope: repositoryroute.ScopeUnsupported}, false
	}
	scope := repositoryroute.ScopeFromRegistration(registered.Operation, registered.Authentication, authority, registered.Repository, registered.Capability)
	return scope, scope.EdgePolicy != repositoryroute.ScopeUnsupported
}

func writeAGSAuthorizationError(writer http.ResponseWriter, authority legacyAGSAuthority, sessionRequired bool) {
	switch authority {
	case legacyAGSAuthorityMissing:
		if sessionRequired {
			http.Error(writer, "workspace session is required", http.StatusUnauthorized)
		} else {
			http.Error(writer, "repository credential is required", http.StatusUnauthorized)
		}
	case legacyAGSAuthoritySession:
		http.Error(writer, "repository credential is required", http.StatusUnauthorized)
	default:
		http.Error(writer, "invalid authorization", http.StatusBadRequest)
	}
}

func (handler *Handler) resolveWorkspaceQualifiedSessionWorkspace(writer http.ResponseWriter, request *http.Request, workspaceID string) bool {
	_, status := authidentity.ParseSessionAuthorization(request.Header.Values("Authorization"))
	if status != authidentity.SessionAuthorizationValid {
		writeAGSAuthorizationError(writer, classifyLegacyAGSAuthorization(request.Header.Values("Authorization")), true)
		return false
	}
	identity, err := handler.identityResolver.ResolveWorkspaceIdentity(request.Context(), WorkspaceIdentityRequest{
		Method: request.Method, Path: request.URL.Path, RawPath: request.URL.RawPath, RawQuery: request.URL.RawQuery,
		WorkspaceQualifiedSessionAuthorization: request.Header.Values("Authorization")[0], Authority: sessionCandidateAuthority, WorkspaceID: workspaceID,
	})
	if err != nil {
		if errors.Is(err, ErrWorkspaceIdentityUnauthorized) {
			http.Error(writer, "unauthorized", http.StatusUnauthorized)
		} else if errors.Is(err, ErrWorkspaceIdentityUnresolved) {
			http.Error(writer, "workspace identity cannot be resolved", http.StatusBadRequest)
		} else {
			http.Error(writer, "workspace identity resolver unavailable", http.StatusServiceUnavailable)
		}
		return false
	}
	if strings.TrimSpace(identity.WorkspaceID) != workspaceID {
		http.Error(writer, "conflicting route workspace identity", http.StatusBadRequest)
		return false
	}
	return true
}
