package constants

import (
	"context"
	"strings"
)

type standardActionContextKey string

const (
	contextKeyStandardActionID             standardActionContextKey = "standard_action_id"
	contextKeyStandardActionRootFlow       standardActionContextKey = "standard_action_root_flow"
	contextKeyStandardActionProtectedRoot  standardActionContextKey = "standard_action_protected_root"
	contextKeyStandardActionAccessScopeSet standardActionContextKey = "standard_action_access_scope_set"
)

// StandardActionInternalAccessScope is a declarative permission exception for
// access owned by a standard action. It can widen read/RLS visibility or bypass
// user object CRUD/FLS only for the matching action, object, and operation; it
// does not grant broad system context or direct-SQL privileges.
type StandardActionInternalAccessScope struct {
	StandardActionID string
	ObjectAPIName    string
	Operation        string
}

// WithStandardActionInternalAccessScopes stamps the active standard action and
// its metadata-owned internal access scopes onto ctx for downstream query and
// persistence calls made by Flow and subflows.
func WithStandardActionInternalAccessScopes(ctx context.Context, actionID string, scopes []StandardActionInternalAccessScope) context.Context {
	actionID = strings.TrimSpace(actionID)
	if actionID == "" {
		return ctx
	}
	ctx = WithStandardActionExecutionContext(ctx, actionID)
	if len(scopes) == 0 {
		return ctx
	}
	scopeSet := make(map[string]struct{}, len(scopes))
	for _, scope := range scopes {
		if !strings.EqualFold(strings.TrimSpace(scope.StandardActionID), actionID) {
			continue
		}
		objectName := strings.TrimSpace(scope.ObjectAPIName)
		operation := strings.TrimSpace(scope.Operation)
		if objectName == "" || operation == "" {
			continue
		}
		scopeSet[standardActionScopeKey(actionID, objectName, operation)] = struct{}{}
	}
	if len(scopeSet) == 0 {
		return ctx
	}
	return context.WithValue(ctx, contextKeyStandardActionAccessScopeSet, scopeSet)
}

func WithStandardActionExecutionContext(ctx context.Context, actionID string) context.Context {
	actionID = strings.TrimSpace(actionID)
	if actionID == "" {
		return ctx
	}
	return context.WithValue(ctx, contextKeyStandardActionID, actionID)
}

func WithStandardActionExecutionRootFlow(ctx context.Context, actionID string, rootFlowName string) context.Context {
	ctx = WithStandardActionExecutionContext(ctx, actionID)
	rootFlowName = strings.TrimSpace(rootFlowName)
	if rootFlowName == "" {
		return ctx
	}
	return context.WithValue(ctx, contextKeyStandardActionRootFlow, rootFlowName)
}

func HasStandardActionExecutionContext(ctx context.Context) bool {
	actionID, _ := ctx.Value(contextKeyStandardActionID).(string)
	return strings.TrimSpace(actionID) != ""
}

func StandardActionExecutionOwnsRootFlow(ctx context.Context, flowName string) bool {
	if !HasStandardActionExecutionContext(ctx) {
		return false
	}
	rootFlowName, _ := ctx.Value(contextKeyStandardActionRootFlow).(string)
	return strings.EqualFold(strings.TrimSpace(rootFlowName), strings.TrimSpace(flowName)) &&
		strings.TrimSpace(flowName) != ""
}

func WithStandardActionProtectedFlowExecution(ctx context.Context, rootFlowName string) context.Context {
	rootFlowName = strings.TrimSpace(rootFlowName)
	if rootFlowName == "" {
		return ctx
	}
	return context.WithValue(ctx, contextKeyStandardActionProtectedRoot, rootFlowName)
}

func HasStandardActionProtectedFlowExecution(ctx context.Context) bool {
	rootFlowName, _ := ctx.Value(contextKeyStandardActionProtectedRoot).(string)
	return strings.TrimSpace(rootFlowName) != ""
}

func WithoutStandardActionProtectedFlowExecution(ctx context.Context) context.Context {
	return context.WithValue(ctx, contextKeyStandardActionProtectedRoot, "")
}

func HasStandardActionInternalAccessScopes(ctx context.Context) bool {
	actionID, _ := ctx.Value(contextKeyStandardActionID).(string)
	if strings.TrimSpace(actionID) == "" {
		return false
	}
	scopeSet, _ := ctx.Value(contextKeyStandardActionAccessScopeSet).(map[string]struct{})
	return len(scopeSet) > 0
}

func WithoutStandardActionInternalAccessScopes(ctx context.Context) context.Context {
	return context.WithValue(ctx, contextKeyStandardActionAccessScopeSet, map[string]struct{}{})
}

// IsScopedInternalAccess returns true only when ctx carries an active
// standard-action id and a matching metadata-owned access scope.
func IsScopedInternalAccess(ctx context.Context, objectAPIName string, operation string) bool {
	actionID, _ := ctx.Value(contextKeyStandardActionID).(string)
	if strings.TrimSpace(actionID) == "" {
		return false
	}
	scopeSet, _ := ctx.Value(contextKeyStandardActionAccessScopeSet).(map[string]struct{})
	if len(scopeSet) == 0 {
		return false
	}
	_, ok := scopeSet[standardActionScopeKey(actionID, objectAPIName, operation)]
	return ok
}

func standardActionScopeKey(actionID string, objectAPIName string, operation string) string {
	return strings.ToLower(strings.TrimSpace(actionID)) + "|" +
		strings.ToLower(strings.TrimSpace(objectAPIName)) + "|" +
		strings.ToLower(strings.TrimSpace(operation))
}
