
    }0=iI                        d Z ddlmZ ddlmZ ddlmZ ddlmZmZ ddl	m
Z
mZmZmZ ddlmZmZmZmZ e
rdd	lmZ dd
lmZ  ed       G d d             Z G d d      Zy)z&Wallet service for point transactions.    )annotations)	dataclass)datetime)DecimalROUND_HALF_UP)TYPE_CHECKINGAnyDictOptional   )InsufficientBalanceErrorInvalidParamsErrorInvalidPointTypeErrorWalletOperationError)Model)WalletRepositoryProtocolT)frozenc                      e Zd ZU dZded<   ded<   ded<   ded<   ded	<   d
ed<   d
ed<   ded<   ded<   ded<   ded<   y)TransactionRecordz'Represents a wallet transaction record.
int | Noneidstrwtypeintiiduidtyper   amountbalance
trans_typedescrcdateDict[str, Any]
extra_dataN)__name__
__module____qualname____doc____annotations__     W/home/cursorai/projects/telegram-earn/packages/wallet_utils/src/wallet_utils/service.pyr   r      s?    1NJ	H	H
IOJJr+   r   c                  H   e Zd ZdZh dZddZ	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ		 	 	 	 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	Z
dd
Z	 	 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZedd       Zy)WalletServicez
    Python port of the legacy WalletPoint PHP class.

    The service works with a WalletRepositoryProtocol that only needs to define point types.
    All balance operations and transaction recording are handled automatically.
    >
   r   r   r   r   r"   r!   r   r   r   r    c                    || _         y)z
        Initialize the wallet service.

        Args:
            repository: WalletRepositoryProtocol instance that defines point types and provides User model
        N)
repository)selfr0   s     r,   __init__zWalletService.__init__>   s     %r+   Nc                f   |i }| j                  d|||||d|       | j                  j                         }|j                  |d      }	t	        dt        |	            }	|j                  t        d      |	z  t              }
	 | j                  j                  |||
d      }t        d|||d
|
|||| j                         |j                  di             }	 | j                  j                  |      }|S # t        $ r}t        d	|       |d}~ww xY w# t        $ r}t        d|       |d}~ww xY w)a  
        Add points to a user wallet balance.

        Args:
            user_id: The user ID
            point_type: The type of wallet to add to (e.g., "credit_balance")
            amount: The amount to add (must be positive)
            remarks: Description/remarks for the transaction
            trans_type: Transaction type code (integer constant, optional)
            params: Extra parameters dict with optional 'data' key for additional fields
            iid: Initiator ID - ID of the user who performed this transaction (-100 for system)

        Returns:
            The transaction ID

        Raises:
            InvalidPointTypeError: If point type doesn't exist
            InvalidParamsError: If params are invalid
            WalletOperationError: If the operation fails
        NaddF   r   0.1roundingallow_negativezError adding user point: cdatar   r   r   r   r   r   r   r    r!   r"   r$   #Error creating transaction record: )_validate_add_deduct_pointr0   get_point_typesgetmaxr   quantizer   r   update_balance	Exceptionr   r   _get_current_datetimecreate_transaction_record)r1   user_id
point_typer   remarksr    paramsr   point_typesdecimal_placesamount_roundedbalance_aftererecordtransaction_ids                  r,   	add_pointzWalletService.add_pointG   sZ   < >F''7JUF	

 oo557$Q7QN 34  >)ITab	O OO::7JP^ot:uM
 #!!!,,.zz&"-
	Y!__FFvNN -  	O&)B1#'FGQN	O&  	Y&)LQC'PQWXX	Ys0   C3 D 3	D<DD	D0D++D0c	                   |i }| j                  d|||||||       | j                  j                         }	|	j                  |d      }
t	        dt        |
            }
|j                  t        d      |
z  t              }	 | j                  j                  ||||      \  }}|sP| j                  j                  ||      }t        |      }t        |      }t        d	|d
|
 dd|d
|
 d||      t        d|||d||||| j!                         |j                  di             }	 | j                  j#                  |      }|S # t        $ r}t        d|       |d}~ww xY w# t        $ r}t        d|       |d}~ww xY w)a-  
        Deduct points from a user wallet balance using atomic SQL operation.

        Uses SQL WHERE clause to prevent race conditions and ensure balance sufficiency.

        Args:
            user_id: The user ID
            point_type: The type of wallet to deduct from
            amount: The amount to deduct (must be positive)
            remarks: Description/remarks for the transaction
            trans_type: Transaction type code (integer constant, optional)
            allow_negative: Whether to allow negative balance (default: False)
            params: Extra parameters dict with optional 'data' key for additional fields
            iid: Initiator ID - ID of the user who performed this transaction (-100 for system)

        Returns:
            The transaction ID

        Raises:
            InsufficientBalanceError: If insufficient balance and allow_negative=False
            InvalidPointTypeError: If point type doesn't exist
            InvalidParamsError: If params are invalid
            WalletOperationError: If the operation fails
        Ndeductr5   r   r6   r7   r9   zError deducting user point: z6Insufficient balance for this transaction. Available: .fz, Requested: )	available	requesteddr<   r=   r>   )r?   r0   r@   rA   rB   r   rC   r   r   deduct_balance_atomicrE   r   get_user_balancefloatr   r   rF   rG   )r1   rH   rI   r   rJ   r    r:   rK   r   rL   rM   rN   successrO   rP   current_balancerX   rY   rQ   rR   s                       r,   deduct_pointzWalletService.deduct_point   s   F >F''gz67JX^	

 oo557$Q7QN 34  >)ITab	R%)__%J%J^N &K &"G] "oo>>w
SOo.IfI*HSTUcTddeReHf g'.)9':;=##	  #!!!,,.zz&"-
	Y!__FFvNN G  	R&)EaS'IJPQQ	R@  	Y&)LQC'PQWXX	Ys0   "E +E( 	E%E  E%(	F1F  Fc                b    | j                   j                         }||vrt        d| d|      y)z
        Validate whether the given point type is valid.

        Raises:
            InvalidPointTypeError: If point type doesn't exist
        No such point (	) exists!rI   N)r0   r@   r   )r1   rH   rI   valid_typess       r,   _validate_user_point_typez'WalletService._validate_user_point_type   s>     oo557[('!*Y7J  )r+   c	                   | j                  ||       |dk(  rdnd}	t        |t              st        d|	 d|	      d|v r|d   rt        |d   t              st        d|	 d|	      g }
|d   j	                         D ]"  }|| j
                  v s|
j                  |       $ |
r%d	j                  |
      }t        d|	 d
| d|	      yyy)z
        Validate parameters for add/deduct point operations.

        Raises:
            InvalidPointTypeError: If point type doesn't exist
            InvalidParamsError: If params are invalid
        r4   rS   r`   zInvalid params for z(), it must be a dict!)methodr<   zInvalid params["data"] for z, z-(), some of the data parameters are invalid: !N)rf   
isinstancedictr   keysRESERVED_FIELDSappendjoin)r1   
add_deductrH   rI   r   rJ   r    r:   rK   rh   invalid_fieldsfield
fields_strs                r,   r?   z(WalletService._validate_add_deduct_point   s    $ 	&&w
; *e 3&$'$':6(BX%YbhiiVvfVnd3(1&9OPY_   N,,.D000"))%0 / !YY~6
(1&9fgqfrrst!   !/r+   c
                   | j                   j                         }
|
j                  j                         }||j	                  |      }|A| j                   j                         }||vrt        d| d|      |j	                  |      }||j	                  |      }|%|dvrt        d	| d
      |j	                  |      }||j	                  |      }|7t        |t              rt        j                  |      }|j	                  |      }|7t        |t              rt        j                  |      }|j	                  |      }|j                  d      }|	dkD  r||	d }||d| }g }|D ]  }t        |j                  |j                  |j                   |j"                  |j$                  |j&                  |j(                  |j*                  |j,                  t/        |j0                  d      r|j0                  j3                         nt        |j0                        |j4                  xs i       }|j7                  |        |S )a  
        Retrieve wallet transaction history with various filters.
        
        Args:
            user_id: Filter by user ID (uid field)
            point_type: Filter by wallet/point type (wtype field)
            trans_type: Filter by transaction type code (trans_type field, integer constant)
            transaction_type: Filter by transaction type ('c' for credit, 'd' for debit) (type field)
            iid: Filter by initiator ID (iid field) - user who performed the transaction (-100 for system)
            start_date: Filter transactions from this date (inclusive) (cdate field)
            end_date: Filter transactions until this date (inclusive) (cdate field)
            limit: Maximum number of records to return
            offset: Number of records to skip (for pagination)
        
        Returns:
            List of TransactionRecord objects
        
        Example:
            # Get all transactions for a user
            transactions = service.get_transaction_history(user_id=123)
            
            # Get credit transactions for a specific point type
            transactions = service.get_transaction_history(
                user_id=123,
                point_type="credit_balance",
                transaction_type="c",
            )
            
            # Get transactions by transaction type code
            from wallet_utils.transaction_types import WALLET_DEPOSIT
            transactions = service.get_transaction_history(
                user_id=123,
                trans_type=WALLET_DEPOSIT,
            )
            
            # Get transactions in date range
            from datetime import datetime, timedelta
            end = datetime.now()
            start = end - timedelta(days=30)
            transactions = service.get_transaction_history(
                user_id=123,
                start_date=start,
                end_date=end,
                limit=100,
            )
        Nr   rb   rc   rd   r   r    r;   rZ   Invalid transaction_type: &. Must be 'c' (credit) or 'd' (debit).r   r   
cdate__gte
cdate__ltez-cdater   	isoformatr=   )r0   get_wallet_modelobjectsallfilterr@   r   r   rj   r   r   fromisoformatorder_byr   r   r   r   r   r   r   r   r    r!   hasattrr"   r   r$   rn   )r1   rH   rI   r    transaction_typer   
start_dateend_datelimitoffsetwallet_modelquerysetre   recordsobjrQ   s                   r,   get_transaction_historyz%WalletService.get_transaction_history$  s8   t 779''++- 73H!//99;K,+%j\;
   Z8H!*=H'z1(01A0BBhi   ,<=H?3/H!*c*%33J?
*=H(C(#11(;(;H $$X. A:(H'H C&66iiGGGGXXzz>>ii/6syy+/Ncii))+TWX[XaXaTb>>/RF NN6"   r+   c                   | j                   j                         }	 |j                  j                  |      }t	        |j
                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  t        |j                  d      r|j                  j!                         nt#        |j                        |j$                  xs i       S # |j&                  $ r Y yw xY w)z
        Get a single transaction by its ID.
        
        Args:
            transaction_id: The transaction ID
        
        Returns:
            TransactionRecord if found, None otherwise
        )r   r   r=   N)r0   r   r   rA   r   r   r   r   r   r   r   r   r    r!   r   r"   r   r   r$   DoesNotExist)r1   rR   r   r   s       r,   get_transaction_by_idz#WalletService.get_transaction_by_id  s     779	&&**n*=C$66iiGGGGXXzz>>ii/6syy+/Ncii))+TWX[XaXaTb>>/R  (( 		s   CC9 9D
Dc                   | j                   j                         }|j                  j                         }	||	j	                  |      }	|A| j                   j                         }
||
vrt        d| d|      |	j	                  |      }	||	j	                  |      }	|%|dvrt        d| d	      |	j	                  |
      }	||	j	                  |      }	|7t        |t              rt        j                  |      }|	j	                  |      }	|7t        |t              rt        j                  |      }|	j	                  |      }	|	j                         S )a  
        Count transactions matching the given filters.
        
        Args:
            user_id: Filter by user ID (uid field)
            point_type: Filter by wallet/point type (wtype field)
            trans_type: Filter by transaction type code (trans_type field, integer constant)
            transaction_type: Filter by transaction type ('c' for credit, 'd' for debit) (type field)
            iid: Filter by initiator ID (iid field) - user who performed the transaction (-100 for system)
            start_date: Filter transactions from this date (inclusive) (cdate field)
            end_date: Filter transactions until this date (inclusive) (cdate field)
        
        Returns:
            Number of transactions matching the filters
        ru   rb   rc   rd   rv   rw   rx   ry   rz   r{   r|   r}   r   )r0   r   r   r   r   r@   r   r   rj   r   r   r   count)r1   rH   rI   r    r   r   r   r   r   r   re   s              r,   count_transactionsz WalletService.count_transactions  sd   2 779''++- 73H!//99;K,+%j\;
   Z8H!*=H'z1(01A0BBhi   ,<=H?3/H!*c*%33J?
*=H(C(#11(;(;H~~r+   c                 F    t        j                         j                         S )z*Get current datetime as ISO format string.)r   nowr   r*   r+   r,   rF   z#WalletService._get_current_datetime  s     ||~''))r+   )r0   z'WalletRepositoryProtocol')NN)rH   r   rI   r   r   r   rJ   r   r    r   rK   Optional[Dict[str, Any]]r   r   returnr   )NFNr   )rH   r   rI   r   r   r   rJ   r   r    r   r:   boolrK   r   r   r   r   r   )rH   r   rI   r   r   None)rp   r   rH   r   rI   r   r   r   rJ   r   r    r   r:   r   rK   r#   r   r   )	NNNNNNNNr   )rH   r   rI   
str | Noner    r   r   r   r   r   r   datetime | str | Noner   r   r   r   r   r   r   zlist[TransactionRecord])rR   r   r   zTransactionRecord | None)NNNNNNN)rH   r   rI   r   r    r   r   r   r   r   r   r   r   r   r   r   )r   r   )r%   r&   r'   r(   rm   r2   rS   r`   rf   r?   r   r   r   staticmethodrF   r*   r+   r,   r.   r.   (   sq   O% "&+/FF F 	F
 F F )F F 
F\ "&$+/[[ [ 	[
 [ [ [ )[ [ 
[z)) ) 	)
 ) ) ) ) ) 
)Z #!%!%'+,0*. || | 	|
 %| | *| (| | | 
!||> #!%!%'+,0*.@ @  @  	@ 
 %@  @  *@  (@  
@ D * *r+   r.   N)r(   
__future__r   dataclassesr   r   decimalr   r   typingr   r	   r
   r   
exceptionsr   r   r   r   django.db.modelsr   r0   r   r   r.   r*   r+   r,   <module>r      sY    , " !  * 5 5  &4 $   \* \*r+   