package lookup

import (
	"context"
	"net/http"
	"net/url"
	"strings"

	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/core/authidentity"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

const sessionCandidateAuthority = "session_candidate"

type sessionAuthenticator interface {
	AuthenticateSession(context.Context, string) (identity.AuthenticatedUser, error)
}

type workspaceAccessResolver interface {
	GetWorkspaceForUser(context.Context, string, string) (workspace.Summary, error)
}

// AuthenticatedResolver extends credential-owned legacy resolution with the
// bounded user-session flow required by native AGS repository requests.
// A caller workspace header is only a claim: session authentication and the
// existing workspace access owner turn it into an authoritative identity.
type AuthenticatedResolver struct {
	legacy     Resolver
	sessions   sessionAuthenticator
	workspaces workspaceAccessResolver
}

func NewAuthenticatedResolver(legacy Resolver, sessions sessionAuthenticator, workspaces workspaceAccessResolver) *AuthenticatedResolver {
	return &AuthenticatedResolver{legacy: legacy, sessions: sessions, workspaces: workspaces}
}

func (resolver *AuthenticatedResolver) ResolveWorkspaceRouteIdentity(
	ctx context.Context,
	method, path, rawPath, rawQuery, canonicalSessionAuthorization, declaredAuthority, claimedWorkspaceID string,
) (string, bool, error) {
	if resolver == nil {
		return "", false, nil
	}
	request := &http.Request{Method: method, URL: &url.URL{Path: path, RawPath: rawPath, RawQuery: rawQuery}}
	request.RequestURI = request.URL.RequestURI()
	if qualifiedPath, err := workspaceurl.Parse(request); err == nil {
		if qualifiedPath.WorkspaceID != strings.TrimSpace(claimedWorkspaceID) {
			return "", false, nil
		}
		return resolver.resolveSessionWorkspace(ctx, canonicalSessionAuthorization, declaredAuthority, qualifiedPath.WorkspaceID)
	}
	scope := repositoryroute.Classify(request)
	if scope.EdgePolicy != repositoryroute.ScopeRepositoryCredentialOrSession && scope.EdgePolicy != repositoryroute.ScopeSessionWorkspace {
		if resolver.legacy != nil {
			return resolver.legacy.ResolveWorkspaceRouteIdentity(ctx, method, path, rawPath, rawQuery, canonicalSessionAuthorization, declaredAuthority, claimedWorkspaceID)
		}
		return "", false, nil
	}
	return resolver.resolveSessionWorkspace(ctx, canonicalSessionAuthorization, declaredAuthority, claimedWorkspaceID)
}

func (resolver *AuthenticatedResolver) resolveSessionWorkspace(ctx context.Context, canonicalSessionAuthorization, declaredAuthority, claimedWorkspaceID string) (string, bool, error) {
	claimedWorkspaceID = strings.TrimSpace(claimedWorkspaceID)
	if !api.IsValidRouteWorkspaceID(claimedWorkspaceID) {
		return "", false, nil
	}
	if resolver.sessions == nil || resolver.workspaces == nil {
		return "", false, nil
	}
	token, status := authidentity.ParseSessionAuthorization([]string{canonicalSessionAuthorization})
	if declaredAuthority != sessionCandidateAuthority || status != authidentity.SessionAuthorizationValid {
		return "", false, storeerr.ErrUnauthorized
	}
	authenticated, err := resolver.sessions.AuthenticateSession(ctx, token)
	if err != nil {
		return "", false, storeerr.ErrUnauthorized
	}
	summary, err := resolver.workspaces.GetWorkspaceForUser(ctx, authenticated.User.ID, claimedWorkspaceID)
	if err != nil || strings.TrimSpace(summary.WorkspaceID) != claimedWorkspaceID {
		return "", false, storeerr.ErrUnauthorized
	}
	return claimedWorkspaceID, true, nil
}
