package runtimeproduction

import (
	"context"
	"database/sql"
	"sort"
	"strings"

	"github.com/nexuscrm/backend/internal/host/adapters/apihost/middleware"
	"github.com/nexuscrm/backend/internal/runtimeidentity"
	kernelauth "github.com/nexuscrm/kernel/pkg/auth"
	"github.com/nexuscrm/shared/pkg/ratelimit"
	"github.com/pingcap-inc/nexuscrm-api-host/hostruntime"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/sessionv1"
)

// sessionDocumentPolicyReader adapts the existing policy owners to the
// authenticated session read. L1 supplies document route facts, never identity,
// scope grants, ownership, or limiter configuration. No second registry/cache
// or product resource list is introduced here.
func sessionDocumentPolicyReader(db *sql.DB) runtimeidentity.SessionDocumentPolicyReader {
	return func(ctx context.Context, claims *kernelauth.Claims, request semantic.DocumentPolicyRequest) (*semantic.DocumentPolicy, error) {
		if err := ctx.Err(); err != nil {
			return nil, err
		}
		if claims == nil {
			return nil, &semantic.Failure{Category: semantic.FailureUnauthenticated}
		}
		result := &semantic.DocumentPolicy{}
		user := claims.User
		subject := middleware.RateLimitSubjectForSession(hostruntime.UserSession{ConnectedAppID: user.ConnectedAppId, SessionClass: user.SessionClass, GrantType: user.GrantType}, claims.ID)
		if subject != "" {
			hourly, daily := ratelimit.Config()
			result.HourlyLimit, result.DailyLimit = int64(hourly), int64(daily)
		}
		// The legacy broad scopes do not consult the custom-scope registry.
		broad := strings.TrimSpace(user.Scope) == ""
		for _, scope := range strings.Fields(user.Scope) {
			if scope == "api" || scope == "full" || scope == "api.read" {
				broad = true
			}
		}
		if broad {
			return result, nil
		}
		if db == nil {
			return nil, &semantic.Failure{Category: semantic.FailureUnavailable}
		}
		registry := middleware.NewScopeRegistry()
		registry.SetRouteFamilyPrefix(request.RouteFamilyPrefix)
		var policies []middleware.ResourceDocumentLinkRoutePolicy
		for _, route := range request.LinkRoutes {
			route := route
			policies = append(policies, middleware.ResourceDocumentLinkRoutePolicyFunc{Link: route.Link, Allows: func(c middleware.ResourceDocumentLinkRouteContext) bool {
				return c.CoversRoute(route.Method, route.Path)
			}})
		}
		registry.SetResourceDocumentLinkRoutePolicies(policies...)
		if err := registry.LoadFromDB(db); err != nil {
			return nil, &semantic.Failure{Category: semantic.FailureUnavailable}
		}
		links, restricted := middleware.ResourceDocumentLinkPolicyForConnectedApp(user.Scope, user.ConnectedAppId, registry)
		result.Restricted = restricted
		for link := range links {
			result.AllowedLinks = append(result.AllowedLinks, link)
		}
		sort.Strings(result.AllowedLinks)
		return result, nil
	}
}
