
    {i&                        d Z ddlmZ ddlZddlmZmZmZmZ ddl	m
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  ede
      Z G d de
ee         Z G d dee         Zy)z
Modern pagination utility for SQLAlchemy queries with Pydantic schemas.

This module provides a QueryPaginator class that handles pagination of SQLAlchemy
queries with automatic serialization to Pydantic models, designed for Python 3.12+.
    )annotationsN)AnyTypeVarGenericUnion)	BaseModel)Query)Select)Session)selectfunc)DEFAULT_PER_PAGEMAX_PER_PAGEResponseSchemaType)boundc                  b    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<   y)PaginationResultz-Pagination result container with type safety.inttotalpageper_page
str | Nonenextpreviousstrfirstlastlist[ResponseSchemaType]resultN)__name__
__module____qualname____doc____annotations__     T/var/www/html/hwPaymentPortal-be-dev/hw-payment-portal-api/src/helpers/pagination.pyr   r      s0    7J
IM
J
I$$r&   r   c                     e Zd ZdZdddedd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZedd       Zedd       Zedd	       Z	edd
       Z
edd       ZdddZedd       Zedd       Zedd       Zedd       ZddZddZddZddZy)QueryPaginatora  
    Modern SQLAlchemy query paginator with Pydantic schema serialization.

    This paginator provides efficient pagination for SQLAlchemy queries with
    automatic conversion to Pydantic models. It supports both ORM and raw query
    results with comprehensive URL generation for navigation links.

    Args:
        query: SQLAlchemy Query object or Select statement to paginate
        schema: Pydantic model class for response serialization
        db: Database session (required for Select statements)
        url: Base URL for generating pagination links
        offset: Number of records to skip (default: 0)
        limit: Number of records to return (default: DEFAULT_PER_PAGE)
        use_orm: Whether to use ORM mode for serialization (default: True)
        model: SQLAlchemy model class (required for Select statements)

    Example:
        >>> # For legacy Query objects
        >>> paginator = QueryPaginator(
        ...     query=session.query(User),
        ...     schema=UserSchema,
        ...     url="/api/users",
        ...     limit=10
        ... )
        >>> 
        >>> # For modern Select statements
        >>> paginator = QueryPaginator(
        ...     query=select(User),
        ...     schema=UserSchema,
        ...     db=session,
        ...     model=User,
        ...     url="/api/users",
        ...     limit=10
        ... )
        >>> result = paginator.paginate()
    Nr   T)dbmodeloffsetlimituse_ormc               R   || _         || _        || _        || _        |j	                  d      | _        t        d|      | _        t        dt        |t                    | _
        || _        t        |t              | _        | j                  r||t        d      d | _        d | _        y )N/r      z=Database session and model are required for Select statements)queryschemar*   r+   rstripurlmaxr,   minr   r-   r.   
isinstancer
   is_select_stmt
ValueError_count_records)	selfr2   r3   r5   r*   r+   r,   r-   r.   s	            r'   __init__zQueryPaginator.__init__L   s     

::c?!VnC|45
 )7 BJ%-\]] #'9=r&   c                >   | j                   | j                  rt        t        j                               j                  | j                  j                  r| j                  j                  d   n| j                        }| j                  j                  %|j                  | j                  j                        }| j                  j                  |      j                         | _         | j                   S | j                  j	                         | _         | j                   S )z$Get total record count with caching.r   )r;   r9   r   r   countselect_fromr2   fromsr+   whereclausewherer*   executescalar)r=   count_querys     r'   r@   zQueryPaginator.countm   s     ;;"" %TZZ\2>>+/::+;+;DJJ$$Q'
 ::))5"-"3"3DJJ4J4J"KK"ggook:AAC {{ #jj..0{{r&   c                :    | j                   | j                  z  dz   S )z$Get current page number (1-indexed).r1   )r,   r-   r=   s    r'   current_pagezQueryPaginator.current_page   s     tzz)Q..r&   c                n    t        dt        j                  | j                  | j                  z              S )zGet total number of pages.r1   )r6   mathceilr@   r-   rI   s    r'   total_pageszQueryPaginator.total_pages   s'     1dii

TZZ 7899r&   c                N    | j                   | j                  z   | j                  k  S )zCheck if there's a next page.)r,   r-   r@   rI   s    r'   has_nextzQueryPaginator.has_next   s      {{TZZ'$**44r&   c                     | j                   dkD  S )z!Check if there's a previous page.r   )r,   rI   s    r'   has_previouszQueryPaginator.has_previous   s     {{Qr&   c                J    |xs | j                   }| j                   d| d| S )a  
        Build pagination URL for the given page.
        
        Args:
            page: Page number (1-indexed)
            per_page: Items per page (defaults to current limit)
            
        Returns:
            Formatted URL with pagination parameters
        z?page=z
&per_page=)r-   r5   )r=   r   r   s      r'   
_build_urlzQueryPaginator._build_url   s-     )tzz((6$z(<<r&   c                X    | j                   sy| j                  | j                  dz         S )z6Get URL for next page, or None if no next page exists.Nr1   )rP   rT   rJ   rI   s    r'   next_urlzQueryPaginator.next_url   s(     }}t001455r&   c                X    | j                   sy| j                  | j                  dz
        S )z>Get URL for previous page, or None if no previous page exists.Nr1   )rR   rT   rJ   rI   s    r'   previous_urlzQueryPaginator.previous_url   s*       t001455r&   c                $    | j                  d      S )zGet URL for first page.r1   )rT   rI   s    r'   	first_urlzQueryPaginator.first_url   s     q!!r&   c                8    | j                  | j                        S )zGet URL for last page.)rT   rN   rI   s    r'   last_urlzQueryPaginator.last_url   s     t//00r&   c           	        | j                   | j                   S | j                  rv| j                  j                  | j                        j	                  | j                        }| j
                  j                  |      j                         j                         }nL| j                  j                  | j                        j	                  | j                        j                         }	 | j                  r:|D cg c]  }| j                  j                  |       c}| _         | j                   S |D cg c]9  }| j                  j                  t        |d      r|j                         n|      ; c}| _         	 | j                   S c c}w c c}w # t        $ r+}t        d| j                  j                    d|       |d}~ww xY w)z
        Retrieve and serialize the paginated records.
        
        Returns:
            List of serialized Pydantic models for the current page
            
        Raises:
            ValueError: If serialization fails
        N_asdictz(Failed to serialize records with schema z: )r<   r9   r2   r,   r-   r*   rE   scalarsallr.   r3   model_validatehasattrr^   	Exceptionr:   r    )r=   paginated_queryraw_recordsrecordes        r'   get_recordszQueryPaginator.get_records   sn    ==$==  "jj//<BB4::NO''///:BBDHHJK **++DKK8>>tzzJNNPK	l||R] ^!;!;F!C ^ }} #.! KK..76S\C]v~~/?cij! }} !_!  	lGH\H\G]]_`a_bcdjkk	ls<   (F 8"FF .F 2>F
0F 
F 	G&F>>Gc                    t        t           | j                  | j                  | j                  | j
                  | j                  | j                  | j                  | j                               S )a  
        Execute pagination and return complete result.
        
        Returns:
            PaginationResult containing all pagination data and serialized records
            
        Example:
            >>> result = paginator.paginate()
            >>> print(f"Page {result.page} of {math.ceil(result.total / result.per_page)}")
            >>> for item in result.result:
            ...     print(item.name)
        r   r   r   r   r   r   r   r   )
r   r   r@   rJ   r-   rV   rX   rZ   r\   rh   rI   s    r'   paginatezQueryPaginator.paginate   sX       23**""ZZ&&..##%	
 		
r&   c                   | j                         }|j                  |j                  |j                  |j                  |j
                  |j                  |j                  |j                  D cg c]  }|j                          c}dS c c}w )a2  
        Convert pagination result to dictionary format.
        
        This method provides backward compatibility with the original implementation
        while maintaining the same response structure.
        
        Returns:
            Dictionary containing pagination metadata and results
        rj   )
rk   r   r   r   r   r   r   r   r   
model_dump)r=   paginated_resultitems      r'   to_dictzQueryPaginator.to_dict   sz      ==?%++$))(11$))(11%++$))5E5L5LMTt(M	
 		
 Ns   ,Bc           
         | j                   j                   d| j                  j                   d| j                   d| j                   d| j
                  | j                   d
S d d
S )z$String representation for debugging.z(schema=z, page=z, per_page=z, total=unknown))	__class__r    r3   rJ   r-   r;   r@   rI   s    r'   __repr__zQueryPaginator.__repr__  s     ~~&&' (kk**+ ,%%& '

| $#';;#:TZZJ	

 AJJ	
r&   )r2   zUnion[Query, Select]r3   ztype[ResponseSchemaType]r5   r   r*   r   r+   r   r,   r   r-   r   r.   boolreturnNone)rw   r   )rw   rv   )N)r   r   r   z
int | Nonerw   r   )rw   r   )rw   r   )rw   r   )rw   z$PaginationResult[ResponseSchemaType])rw   zdict[str, Any])r    r!   r"   r#   r   r>   propertyr@   rJ   rN   rP   rR   rT   rV   rX   rZ   r\   rh   rk   rp   ru   r%   r&   r'   r)   r)   %   sE   $X %>#> )> 	> > > > > > 
>B  ( / / : : 5 5  = 6 6 6 6 " " 1 1$L
0
,	
r&   r)   )r#   
__future__r   rL   typingr   r   r   r   pydanticr   sqlalchemy.orm.queryr	   sqlalchemy.sqlr
   sqlalchemy.ormr   
sqlalchemyr   r   src.core.utils.enumsr   r   r   r   r)   r%   r&   r'   <module>r      sb    #  / /  & ! " # ? 1C 
%y'*<"= 
%t
W/0 t
r&   