//go:build crm

package crm

import (
	"net/http"
	"strings"

	"github.com/nexuscrm/backend/pkg/serverruntime"
)

// isSalesforceDataAPIPath is the single classifier for the Salesforce data-API route
// family. It covers BOTH the bare discovery root and the versioned tree. Every CRM owner
// of this family — CORS, the internal-error projector — derives from this one predicate
// so they cannot drift.
func isSalesforceDataAPIPath(path string) bool {
	path = strings.TrimSpace(path)
	return path == "/services/data" || strings.HasPrefix(path, "/services/data/")
}

func allowSalesforceDataAPIDynamicCORS(ctx serverruntime.DynamicCORSRouteContext) bool {
	if !isSalesforceDataAPIPath(ctx.Path) {
		return false
	}
	switch strings.ToUpper(strings.TrimSpace(ctx.RequestedMethod)) {
	case http.MethodGet, http.MethodHead:
		return true
	default:
		return false
	}
}

func allowSalesforceRootIdentityDynamicCORS(ctx serverruntime.DynamicCORSRouteContext) bool {
	path := strings.TrimSpace(ctx.Path)
	if path != "/id" && !strings.HasPrefix(path, "/id/") {
		return false
	}
	switch strings.ToUpper(strings.TrimSpace(ctx.RequestedMethod)) {
	case http.MethodGet, http.MethodHead:
		return true
	default:
		return false
	}
}

func allowSalesforceOpenIDScopeRoute(ctx serverruntime.OAuthScopeRouteContext) bool {
	switch strings.ToUpper(strings.TrimSpace(ctx.Method)) {
	case http.MethodGet, http.MethodHead:
	default:
		return false
	}
	switch strings.TrimSpace(ctx.FullPath) {
	case "/id/:orgId/:userId", "/services/data/:version/id/:orgId/:userId":
		return true
	default:
		return false
	}
}

func allowSalesforceChatterAPIScopeRoute(ctx serverruntime.OAuthScopeRouteContext) bool {
	fullPath := strings.TrimSpace(ctx.FullPath)
	return strings.Contains(fullPath, "/connect/") || strings.HasSuffix(fullPath, "/connect")
}

// allowExternalRecordsMCPScopeRoute lets the read-only external.records.read
// scope cover the hosted MCP endpoint. The MCP transport is POST (JSON-RPC), but
// the endpoint exposes only read-only, FLS/RLS-enforced tools, so this is the
// product's explicit assertion that this POST route is a read surface — which is
// why coverage is contributed here (product layer) rather than as a DB-backed
// public scope (those are restricted to GET/HEAD in L0).
func allowExternalRecordsMCPScopeRoute(ctx serverruntime.OAuthScopeRouteContext) bool {
	return strings.ToUpper(strings.TrimSpace(ctx.Method)) == http.MethodPost &&
		strings.TrimSpace(ctx.FullPath) == "/mcp"
}

func buildSalesforceRootIdentityAuthChallenge(ctx serverruntime.AuthChallengeContext) (serverruntime.AuthChallenge, bool) {
	if !ctx.MissingToken {
		return serverruntime.AuthChallenge{}, false
	}
	path := strings.TrimSpace(ctx.Path)
	if path != "/id" && !strings.HasPrefix(path, "/id/") {
		return serverruntime.AuthChallenge{}, false
	}
	return serverruntime.AuthChallenge{
		Status:  http.StatusForbidden,
		Code:    SFCodeMissingOAuthToken,
		Message: "Missing OAuth token",
	}, true
}
