
    $i,                        S r SSKJr  SSKrSSKJrJr  SSKrSSKJ	r	  SSK
Jr  \	" \5      r\(       a  SSKJr   " S S	\5      r " S
 S\5      r " S S5      rSS jrg)zTAB Affiliates API client with retry logic.

Handles retries, rate limiting, and error handling for the public TAB API.
No authentication required - this is a public API.
    )annotationsN)TYPE_CHECKINGAny)
get_logger)get_settingsMockTABClientc                      \ rS rSrSrSrg)TABClientError   z%Base exception for TAB client errors. N__name__
__module____qualname____firstlineno____doc____static_attributes__r       ?/root/tipsharks/tipsharks-elo-api/packages/tab_client/client.pyr   r      s    /r   r   c                      \ rS rSrSrSrg)TABRateLimitError   zRate limit exceeded.r   Nr   r   r   r   r   r      s    r   r   c                      \ rS rSrSrS rS rS rS rS r	  S         SS	 jjr
  S         SS
 jjrSS jrSS jr   S           SS jjrSrg)	TABClient#   a  Client for TAB Affiliates API with retry logic.

The TAB Affiliates API is a public API that provides racing data
for NZ and international racing. No authentication is required.

Example:
    >>> async with TABClient() as client:
    >>>     meetings = await client.get_meetings("2024-01-01", "2024-01-07")
    >>>     for meeting in meetings:
    >>>         event_id = meeting["races"][0]["id"]
    >>>         event = await client.get_event(event_id)
c                f    [        5       U l        U R                  R                  U l        SU l        g)z$Initialize TAB client with settings.N)r   settingstab
tab_config_clientselfs    r   __init__TABClient.__init__1   s#    $--++15r   c                B   #    U R                  5       I Sh  vN   U $  N7f)zAsync context manager entry.N)_ensure_clientr"   s    r   
__aenter__TABClient.__aenter__7   s!     !!### 	$s   c                @   #    U R                  5       I Sh  vN   g N7f)zAsync context manager exit.N)close)r#   exc_typeexc_valexc_tbs       r   	__aexit__TABClient.__aexit__<   s     jjls   c                   #    U R                   c/  [        R                  " U R                  R                  SS9U l         gg7f)z"Ensure HTTP client is initialized.NT)timeoutfollow_redirects)r!   httpxAsyncClientr    r2   r"   s    r   r'   TABClient._ensure_client@   s6     << ,,//!%DL  s   >A c                ~   #    U R                   b*  U R                   R                  5       I Sh  vN   SU l         gg N7f)zClose HTTP client.N)r!   acloser"   s    r   r+   TABClient.closeH   s4     <<#,,%%'''DL $'s   +=;=Nc                  #    U R                  5       I Sh  vN   U R                  R                   SU 3n[        R	                  SU SU 3UUS.S9   U R
                  R                  XUS9I Sh  vN n[        R	                  SUR                   3UR                  US	.S9  UR                  S
:X  a}  X@R                  R                  :  a  [        S5      eSU-  n[        R                  SU S35        [        R                  " U5      I Sh  vN   U R                  XX4S-   5      I Sh  vN $ UR                  S:X  a;   UR                  5       nUR                  SUR                   5      n	[%        SU	 35      eUR                  S:X  a  [%        SU 35      eUR'                  5         UR                  5       $  GN GNL N N! ["         a    UR                   n	 Nnf = f! [(        R*                   a  n
X@R                  R                  :  a  [%        SU
 35      U
eSU
R,                  R                  s=::  a  S:  az  O  OwSU-  n[        R                  SU
R,                  R                   SU S35        [        R                  " U5      I Sh  vN    U R                  XX4S-   5      I Sh  vN  s Sn
A
$ [%        SU
 35      U
eSn
A
f[(        R.                   a  n
X@R                  R                  :  a  [%        SU
 35      U
eSU-  n[        R                  SU S35        [        R                  " U5      I Sh  vN    U R                  XX4S-   5      I Sh  vN  s Sn
A
$ Sn
A
ff = f7f)a)  Make HTTP request with retry logic.

Args:
    method: HTTP method (GET, POST, etc.)
    endpoint: API endpoint path (without base URL)
    params: Query parameters
    retry_count: Current retry attempt

Returns:
    Response JSON data

Raises:
    TABClientError: If request fails after retries
N/zMaking z request to )endpointparams)extra)r=   zResponse status: )status_coder<   i  z(Rate limit exceeded, max retries reached   zRate limited, waiting zs before retry   i  errorzBad request: i  zResource not found: zHTTP error: i  iX  zServer error z
, waiting zRequest error: zRequest failed, waiting )r'   r    base_urlloggerdebugr!   requestr?   max_retriesr   warningasynciosleep_request_with_retryjsongettext	Exceptionr   raise_for_statusr4   HTTPStatusErrorresponseRequestError)r#   methodr<   r=   retry_counturlrR   	wait_time
error_body	error_msges              r   rK   TABClient._request_with_retryN   sL    * !!###))*!H:6 	fX\#/$  	 	
@	!\\11&f1MMHLL#H$8$8#9:&.&:&:Q   ##s*//"="==+,VWW {N	!7	{.QRmmI...!55fAo  
 ##s*.!)J *w FI %}YK%@AA ##s*$';H:%FGG%%'==?"a 	$ N / ! . (I. $$ 	<oo999$|A3%78a? ajj,,2s2{N	#AJJ$:$:#; <(k9 mmI...!55fAo    !<s!34!;!! 		oo999$qc%:;B;INN5i[OP--	***11&/   		s   M-F:<M-G  1F=2BG  G G  ,G-G  0M-1G  ,G .AG  9M-=G   G  G  GG  GG   M*4BKJK,J/-K1M*2M-7KM*AM%<L?=M%MM%M* M-%M**M-c           
     d  #    Uc  U R                   R                  nUc  U R                   R                  n[        R	                  SU SU SU SU 35        UUUUS.nU R                  SSU5      I Sh  vN nUR                  S	/ 5      n[        R	                  S
[        U5       S35        U$  N:7f)a4  Fetch meetings within date range.

Args:
    date_from: Start date (YYYY-MM-DD)
    date_to: End date (YYYY-MM-DD)
    category: Racing category filter - "T" (Thoroughbred),
              "H" (Harness), "G" (Greyhound). If None, uses config default.
    country: Country filter (e.g., "NZ", "AUS"). If None, uses config default.

Returns:
    List of meeting data dictionaries

Example:
    >>> async with TABClient() as client:
    >>>     meetings = await client.get_meetings(
    >>>         "2024-01-01", "2024-01-07", category="H", country="NZ"
    >>>     )
Nz	Fetching z meetings from  to z in )	date_fromdate_tocategorycountryGETzracing/meetingsmeetingszFound z	 meetings)r    default_categorydefault_countryrD   inforK   rM   len)r#   r^   r_   r`   ra   r=   rR   rc   s           r   get_meetingsTABClient.get_meetings   s     4 77H?oo55Gz4y I	
 # 	
 11%9JFSS <<
B/fS]O956 Ts   A1B03B.4;B0c                   #    [         R                  SU 35        U R                  SSU 35      I Sh  vN nUR                  S/ 5      nU(       d  [	        SU 35      eUS   $  N07f)a;  Fetch single meeting details with race summaries.

Args:
    meeting_id: TAB meeting ID (string)

Returns:
    Meeting data dictionary with 'races' array (summaries only)

Note:
    The races in this response only contain summary info (id, race_number).
    Use get_event() to fetch full race details with runners.
zFetching meeting rb   zracing/meetings/Nrc   zMeeting not found: r   )rD   rf   rK   rM   r   )r#   
meeting_idrR   rc   s       r   get_meetingTABClient.get_meeting   st      	'
|4511%j\2
 

 <<
B/ #6zl!CDD{
s   1A&A$1A&c                   #    [         R                  SU 35        U R                  SSU 35      I Sh  vN nUR                  S0 5      nU(       d  [         R	                  SU S35        U$  N87f)aR  Fetch race/event details with runners and results.

Args:
    event_id: TAB event ID (string)

Returns:
    Event data dictionary with:
        - race: Race information (distance, start_type, etc.)
        - runners: List of runners with horse, driver, trainer info
        - results: List of results with positions (if race is finished)
        - dividends: Dividend information (if available)

Example:
    >>> async with TABClient() as client:
    >>>     event = await client.get_event("abc123")
    >>>     runners = event.get("runners", [])
    >>>     results = event.get("results", [])
zFetching event rb   zracing/events/NdatazEvent z returned empty data)rD   rf   rK   rM   rH   )r#   event_idrR   ro   s       r   	get_eventTABClient.get_event   sl     & 	ohZ0111%>(9TUU ||FB'NNVH:-ABC Vs   1A.A,9A.c                  #    Uc  U R                   R                  nUc  U R                   R                  n[        R	                  SU SU SU SU S3	5        UUUU[        US5      S.nU R                  S	S
U5      I Sh  vN nU$  N7f)aE  Fetch list of races with pagination.

This is an alternative to get_meetings() that returns races directly
with more filtering options.

Args:
    date_from: Start date (YYYY-MM-DD)
    date_to: End date (YYYY-MM-DD)
    meet_types: Racing type filter - "T", "H", "G" (can be combined)
    countries: Country filter - "NZ", "AUS", etc.
    limit: Maximum results per page (max 200)

Returns:
    Dictionary with:
        - races: List of race data
        - page_token: Token for next page (if more results)

Note:
    For paginated results, use page_token in subsequent requests.
NzFetching race list from r]   z (types=z, countries=)   )r^   r_   
meet_types	countrieslimitrb   zracing/list)r    rd   re   rD   rf   minrK   )r#   r^   r_   rv   rw   rx   r=   rR   s           r   get_races_listTABClient.get_races_list  s     8 99J77I&ykgY ? \i[;	
 #$"_
 11%OO Ps   A=B?B B)r!   r   r    )Nr   )
rT   strr<   r|   r=   zdict[str, Any] | NonerU   intreturndict[str, Any])NN)
r^   r|   r_   r|   r`   
str | Nonera   r   r~   zlist[dict[str, Any]])rk   r|   r~   r   )rp   r|   r~   r   )NNd   )r^   r|   r_   r|   rv   r   rw   r   rx   r}   r~   r   )r   r   r   r   r   r$   r(   r/   r'   r+   rK   rh   rl   rq   rz   r   r   r   r   r   r   #   s    6
  )-bb b &	b
 b 
bP  $"00 0 	0
 0 
0d4H "& $// / 	/
 / / 
/ /r   r   c                     [        5       n U R                  R                  (       a"  SSKJn  [
        R                  S5        U" 5       $ [        5       $ )zGet TAB client (real or mock based on settings).

Returns:
    TABClient or MockTABClient instance

Example:
    >>> async with get_client() as client:
    >>>     meetings = await client.get_meetings("2024-01-01", "2024-01-07")
r   r   z(Using MockTABClient (TAB_MOCK_MODE=true))r   r   	mock_modepackages.tab_client.mock_clientr	   rD   rf   r   )r   r	   s     r   
get_clientr   P  s8     ~H||A>?{r   )r~   zTABClient | MockTABClient)r   
__future__r   rI   typingr   r   r4   packages.core.common.loggingr   packages.core.common.settingsr   r   rD   r   r	   rO   r   r   r   r   r   r   r   <module>r      sX    #  %  3 6	H	=	Y 		 	j jZ	r   