
    Hi7                       U d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	m
Z
mZmZmZmZmZ ddlmZmZ  ej&                  e      Z G d	 d
e      Z ed       G d d             Ze G d d             Z G d de      Ze G d d             Z G d d      Z G d d      Zdaded<   ddZ	 	 d 	 	 	 	 	 	 	 	 	 	 	 d!dZ d"dZ!d#dZ"y)$z Core hook system implementation.    )annotationsN)	dataclassfield)Enum)AnyCallableDictListOptionalProtocol   )HookExecutionErrorHookRejectionErrorc                      e Zd ZdZdZdZy)HookTypezHook execution timing types.prepostN)__name__
__module____qualname____doc__PREPOST     P/home/cursorai/projects/django-wallet-utils/ai-output/hooks/django_hooks/core.pyr   r      s    &
CDr   r   T)frozenc                  8    e Zd ZU dZded<    ee      Zded<   y)HookContexta  
    Immutable context passed to hooks containing operation details.

    This is a base class that should be extended by specific implementations
    to add domain-specific fields.

    All operation parameters are frozen to prevent hooks from modifying them.
    Use the metadata dict to share data between hooks.

    Attributes:
        operation: The operation being performed (e.g., 'add', 'delete', 'execute')
        metadata: Mutable dictionary for inter-hook communication and data sharing

    Example:
        @dataclass(frozen=True)
        class CronjobHookContext(HookContext):
            job_id: str
            schedule: str
            command: str
            user_id: int
    str	operation)default_factoryDict[str, Any]metadataN)r   r   r   r   __annotations__r   dictr$   r   r   r   r   r      s    , N$T:Hn:r   r   c                  D    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)PostHookErrora  
    Represents an error that occurred in a POST hook.

    POST hooks cannot reject operations, but their errors are captured
    and returned so they can be logged or displayed without affecting
    the operation's success.

    Attributes:
        hook_name: Name of the hook that raised the error
        error_code: Machine-readable error code (ALL_CAPS)
        message: Human-readable error message
        details: Additional error context
        exception: The original exception that was raised
    r    	hook_name
error_codemessager#   details	Exception	exceptionN)r   r   r   r   r%   r   r   r   r(   r(   2   s$     NOLr   r(   c                      e Zd ZdZddZy)HookFunctionz3Protocol defining the signature for hook functions.c                     y)a  
        Execute hook logic.

        Args:
            context: Immutable operation context with mutable metadata

        Returns:
            - For PRE hooks: True to continue, False to reject, or raise HookRejectionError
            - For POST hooks: Return value is ignored

        Raises:
            HookRejectionError: To reject operation with error code (PRE hooks only)
            Exception: Any other exception is wrapped in HookExecutionError
        Nr   )selfcontexts     r   __call__zHookFunction.__call__M   s     	r   N)r3   r   returnzbool | None)r   r   r   r   r4   r   r   r   r0   r0   J   s
    =r   r0   c                  H    e Zd ZU dZded<   ded<   ded<   ded<   d	Zd
ed<   y)HookaW  
    Represents a registered hook.

    Attributes:
        name: Unique identifier for the hook
        hook_type: When the hook executes (PRE or POST)
        operation: Which operation triggers this hook ('*' = all operations)
        callback: The function to execute
        priority: Execution order (lower number = higher priority)
    r    namer   	hook_typer!   r0   callbackd   intpriorityN)r   r   r   r   r%   r=   r   r   r   r7   r7   _   s)    	 INHcr   r7   c                  V    e Zd ZdZdd	dZ	 	 d
	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZy)HookRegistrya  
    Registry for managing hooks with priority support.

    Hooks are stored per operation type and executed by priority (lower = higher priority).

    Example:
        registry = HookRegistry(operations=['schedule', 'execute', 'pause', 'delete'])
        registry.register(
            name='check_permissions',
            hook_type=HookType.PRE,
            callback=check_permissions_func,
            operation='delete',
            priority=10
        )
    Nc                H    dg i| _         |r|D ]  }g | j                   |<    yy)z
        Initialize hook registry.

        Args:
            operations: List of valid operation names. If None, defaults to ['*']
        *N)_hooks)r2   
operationsops      r   __init__zHookRegistry.__init__   s.     /22Y "$B ! r   c           
        || j                   vr8t        d| ddj                  | j                   j                                      | j                   j	                         D ]%  }t        fd|D              st        d d       t        ||||      }| j                   |   j                  |       | j                   |   j                  d 	       t        j                  d
|j                   d d| d|        y)a  
        Register a new hook.

        Args:
            name: Unique identifier for the hook
            hook_type: HookType.PRE or HookType.POST
            callback: Function to execute
            operation: Operation name or '*' for all operations
            priority: Execution priority (lower number = higher priority, default=100)

        Raises:
            ValueError: If operation is invalid or hook name already exists

        Example:
            registry.register(
                name="check_daily_limit",
                hook_type=HookType.PRE,
                callback=check_daily_limit_hook,
                operation="execute",
                priority=50,
            )
        zInvalid operation: z. Must be one of: z, c              3  <   K   | ]  }|j                   k(    y wNr8   ).0hr8   s     r   	<genexpr>z(HookRegistry.register.<locals>.<genexpr>   s     15a166T>5s   zHook with name 'z' already registered)r8   r9   r!   r:   r=   c                2    | j                   | j                  fS rH   r=   r8   rK   s    r   <lambda>z'HookRegistry.register.<locals>.<lambda>   s    1::qvv2Fr   keyzRegistered z hook 'z' for operation 'z' with priority N)rB   
ValueErrorjoinkeysvaluesanyr7   appendsortloggerinfovalue)r2   r8   r9   r:   r!   r=   hookshooks    `      r   registerzHookRegistry.register   s   < DKK'%i[0B499T[[M]M]M_C`Bab 
 [['')E1511 #3D69M!NOO * 
 	I%%d+I##(F#G)//*'$7HScdlcmn	
r   c           	         | j                   j                         D ]K  \  }}|D ]A  }|j                  |k(  s|j                  |       t        j                  d| d| d         y M y)z
        Unregister a hook by name.

        Args:
            name: Hook name to remove

        Returns:
            True if hook was found and removed, False otherwise
        zUnregistered hook 'z' from operation ''TF)rB   itemsr8   removerZ   r[   )r2   r8   r!   r]   r^   s        r   
unregisterzHookRegistry.unregister   sh     !% 1 1 3Iu99$LL&KK"5dV;Mi[XY Z[	  !4 r   c                    | j                   j                  |g       }| j                   j                  dg       }||z   }|D cg c]  }|j                  |k(  s| }}|j                  d        |S c c}w )a  
        Get all hooks for a specific operation and type, sorted by priority.

        Args:
            operation: Operation name
            hook_type: HookType.PRE or HookType.POST

        Returns:
            List of hooks sorted by priority (lower number first)
        rA   c                2    | j                   | j                  fS rH   rN   rO   s    r   rP   z(HookRegistry.get_hooks.<locals>.<lambda>   s    QZZ$8r   rQ   )rB   getr9   rY   )r2   r!   r9   operation_hooksglobal_hooks	all_hooksrK   filtereds           r   	get_hookszHookRegistry.get_hooks   ss     ++//)R8{{sB/ $l2	(Ey!AKK9,DAyE 	89 Fs   A2A2c                    | j                   D ]  }| j                   |   j                          ! t        j                  d       y)z0Clear all registered hooks (useful for testing).zCleared all hooksN)rB   clearrZ   r[   )r2   r!   s     r   rn   zHookRegistry.clear   s2    IKK	"((* %'(r   rH   )rC   zOptional[List[str]]rA   r;   r8   r    r9   r   r:   r0   r!   r    r=   r<   r5   Noner8   r    r5   bool)r!   r    r9   r   r5   z
List[Hook]r5   rq   )	r   r   r   r   rE   r_   rd   rl   rn   r   r   r   r?   r?   s   sc     %$ 6
6
 6
 	6

 6
 6
 
6
p$0)r   r?   c                  *    e Zd ZdZdddZddZd	dZy)
HookManagera  
    Manages hook execution for operations.

    Executes PRE hooks before operation (can reject) and POST hooks after (cannot reject).

    Example:
        manager = HookManager(registry)
        
        # Before operation
        context = MyHookContext(operation='delete', user_id=123)
        try:
            manager.execute_pre_hooks(context)
        except HookRejectionError as e:
            return {"error": e.error_code, "message": e.message}
        
        # Perform operation
        result = perform_operation()
        
        # After operation
        errors = manager.execute_post_hooks(context)
        return {"success": True, "post_hook_errors": errors}
    Nc                *    |xs
 t               | _        y)z
        Initialize hook manager.

        Args:
            registry: Hook registry to use. If None, uses global registry.
        N)r?   registry)r2   rx   s     r   rE   zHookManager.__init__  s     !2LNr   c           
        | j                   j                  |j                  t        j                        }|D ]  }t
        j                  d|j                   d|j                          	 |j                  |      }|du rBt        d|j                  j                          d|j                   d|j                  i      t
        j                  d|j                   d	        y# t        $ r  t        $ r^}t
        j                  d|j                   d
| d       t        d|j                   dt        |       |j                  |      |d}~ww xY w)a  
        Execute all PRE hooks for the given operation.

        PRE hooks can reject the operation by:
        1. Returning False
        2. Raising HookRejectionError with error_code and message

        Args:
            context: Operation context

        Raises:
            HookRejectionError: If any hook rejects the operation
            HookExecutionError: If a hook fails unexpectedly
        zExecuting PRE hook '' for FHOOK_REJECTED_zOperation rejected by hook: r)   )r*   r+   r,   z
PRE hook '' completed successfully' failed with error: Texc_infoHook 'z' failed to execute: )r+   r)   original_exceptionN)rx   rl   r!   r   r   rZ   debugr8   r:   r   upperr-   errorr   r    )r2   r3   r]   r^   resultes         r   execute_pre_hookszHookManager.execute_pre_hooks  s=    ''(9(98<<HDLL/		{&ARAR@STUw/ U?,%3DIIOO4E3F#G">tyyk J!,dii 8  z$))4LMN   &  z$))4I!MX\]($TYYK/DSVHM"ii'( 	s   *A:C''E9AEEc                   | j                   j                  |j                  t        j                        }g }|D ]f  }t
        j                  d|j                   d|j                          	 |j                  |       t
        j                  d|j                   d       h |S # t        $ r}t
        j                  d|j                   d|j                   d|j                          |j                  t        |j                  |j                  |j                  |j                  |             Y d}~d}~wt         $ r}t
        j#                  d|j                   d	| d
       |j                  t        |j                  dd|j                   dt%        |       dt'        |      j(                  i|             Y d}~d}~ww xY w)a  
        Execute all POST hooks for the given operation.

        POST hooks cannot reject the operation. Errors are captured and returned
        so the operation remains successful but errors can be logged/displayed.

        Args:
            context: Operation context (typically with updated state after operation)

        Returns:
            List of errors that occurred in POST hooks (empty if all succeeded)
        zExecuting POST hook 'rz   zPOST hook 'r|   z' tried to reject operation: z - )r)   r*   r+   r,   r.   Nr}   Tr~   HOOK_EXECUTION_ERRORr   z
' failed: exception_type)rx   rl   r!   r   r   rZ   r   r8   r:   r   warningr*   r+   rX   r(   r,   r-   r   r    typer   )r2   r3   r]   errorsr^   r   s         r   execute_post_hookszHookManager.execute_post_hooksI  s    ''(9(98==I&(DLL06'BSBSATUVg&{499+5MNO F 9 & !$)),I!,,WZ[\[d[dZef !"&))#$<< !		 !		"#   {499+5J1#NY]^!"&))#9"(:c!fX F!1473C3C D"# s&   ,4B$$	G
-BD55G
A>GG
rH   )rx   Optional[HookRegistry])r3   r   r5   rq   )r3   r   r5   zList[PostHookError])r   r   r   r   rE   r   r   r   r   r   rv   rv      s    .3,\3r   rv   r   _global_registryc                 .    t         
t               a t         S )zd
    Get the global hook registry.

    Lazily initializes the global registry on first access.
    )r   r?   r   r   r   get_global_registryr     s     '>r   c                >    t               j                  | ||||       y)aR  
    Register a hook in the global registry.

    Args:
        name: Unique identifier for the hook
        hook_type: HookType.PRE or HookType.POST
        callback: Function to execute
        operation: Operation name or '*' for all operations
        priority: Execution priority (lower number = higher priority, default=100)

    Example:
        def check_quota(context: HookContext) -> bool:
            if context.metadata.get('count', 0) > 100:
                raise HookRejectionError(
                    error_code="QUOTA_EXCEEDED",
                    message="You have exceeded your quota limit"
                )
            return True

        register_hook(
            name="quota_check",
            hook_type=HookType.PRE,
            callback=check_quota,
            operation="create",
            priority=10,
        )
    N)r   r_   )r8   r9   r:   r!   r=   s        r   register_hookr     s    D ""4HiRr   c                4    t               j                  |       S )z
    Unregister a hook from the global registry.

    Args:
        name: Hook name to remove

    Returns:
        True if hook was found and removed, False otherwise
    )r   rd   rI   s    r   unregister_hookr     s      ++D11r   c                 4    t               j                          y)z>Clear all hooks from the global registry (useful for testing).N)r   rn   r   r   r   clear_hooksr     s    !r   )r5   r?   ro   rp   rr   rt   )#r   
__future__r   loggingdataclassesr   r   enumr   typingr   r   r	   r
   r   r   
exceptionsr   r   	getLoggerr   rZ   r   r   r(   r0   r7   r?   rv   r   r%   r   r   r   r   r   r   r   <module>r      s   & "  (  @ @ >			8	$t  $; ; ;6   .8 *   &D) D)NB BL ,0 ( /	  "S
"S"S "S 	"S
 "S 
"SJ
2"r   