Ë
    §‘ˆje  ã                   ó*  — d Z ddlZddlZddlmZmZ ddlmZmZ ddl	m
Z
mZ ddlmZ  ej                  e«      Zh d£ZdZ G d	„ d
e«      Zdededefd„Zddœdededededef
d„Zdedededdfd„Zddœdedededej6                  dz  dee   f
d„Zy)zÒFetches a catalog from an authenticated HTTP product API.

Unlike Shopify, the shape is unknown, so the mapping comes from the source's
stored config. C's LLM authors that config; this module only executes it.
é    N)Úapply_field_mapÚresolve_path)Úinfer_url_templateÚpropose_field_map)ÚKIND_HTTP_APIÚupdate_source_config)Úassert_safe_url>   úsold outúout of stockÚ
outofstockÚunavailableiè  c                   ó   — e Zd ZdZy)ÚHttpSourceErrorz+The source is misconfigured or unreachable.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    ú8/var/www/html/strategist-ai/app/services/catalog/http.pyr   r      s   „ Ú5r   r   ÚcredentialsÚconfigÚreturnc                 óB  — |j                  d«      xs i }|j                  dd«      }|dk(  ri S | j                  d«      xs | j                  d«      }|st        d«      ‚|dk(  rdd	|› �iS |d
k(  r"|j                  d
«      }|st        d«      ‚||iS t        d|› �«      ‚)NÚauthÚstyleÚnoneÚapi_keyÚaccess_tokenz&auth style requires a key, none storedÚbearerÚAuthorizationzBearer Úheaderz(header auth style requires a header namezunknown auth style: )Úgetr   )r   r   r   r   Úkeyr"   s         r   Úauth_headersr%   !   s·   € Ø�:‰:�fÓÒ# €DØ�H‰H�W˜fÓ%€EØ�‚Øˆ	Ø
�/‰/˜)Ó
$Ò
G¨¯©¸Ó(G€CÙÜÐFÓGÐGØ�ÒØ 7¨3¨% Ð1Ð1Ø�ÒØ—‘˜(Ó#ˆÙÜ!Ð"LÓMÐMØ˜ˆ}ÐÜ
Ð0°°Ð8Ó
9Ð9r   T)Úrequire_currencyÚrecordÚ
source_refr&   c          	      ód  — |j                  d«      s|rt        d«      ‚t        | |d   «      }|j                  d«      }|j                  d«      }d }|�:|r8	 t        |«      }d|cxk  rdk  r!n nt	        t        |«      d|dz  z
  z  d	«      }|j                  d
«      xs dj                  «       j                  «       }|t        v}	i }
dD ]F  \  }}|j                  |«      }t        |t        «      sŒ(|sŒ+|D �cg c]  }t        |«      ‘Œ c}|
|<   ŒH i d|j                  d«      “d|j                  d«      “d|j                  d«      “d|j                  d«      “dd “d|j                  d«      “d|j                  d«      “d|j                  d«      “dd “d|j                  d«      xs g “dg “dd“dd“d|j                  d«      |||	d i dœg“dg “d d!“d"|“|
|j                  d#«      |j                  d$«      |j                  d%«      d&œ¥S # t
        t        t        f$ r d }Y �Œ›w xY wc c}w )'NÚcurrencyz%source config must declare a currencyÚfieldsÚpriceÚdiscount_percentager   éd   é   é   ÚavailabilityÚ ))Úrelated_idsÚrelated)Úcross_sellsr5   )Úupsellsr6   Úexternal_idÚtitleÚdescriptionÚbrandÚhandleÚproduct_urlÚ	image_urlÚraw_categoryÚproduct_typeÚtagsÚcollectionsÚstatusÚACTIVEÚtracks_inventoryTÚvariantsÚsku)rF   r,   Úcompare_at_priceÚ	availableÚinventory_quantityÚoptionsÚattributes_rawÚsource_kindÚhttp_apir(   ÚratingÚreview_countÚfeatured_rank)Útenant_relationsrN   rO   rP   )r#   r   r   ÚfloatÚroundÚ	TypeErrorÚ
ValueErrorÚZeroDivisionErrorÚstripÚlowerÚ_OUT_OF_STOCKÚ
isinstanceÚlistÚstr)r'   r   r(   r&   Úmappedr,   ÚdiscountÚ
compare_atr1   rH   Ú	relationsÚtargetr$   ÚvaluesÚvs                  r   Úto_source_productrd   3   s¤  € ð �:‰:�jÔ!Ñ&6ÜÐEÓFÐFä˜V V¨HÑ%5Ó6€Fà�J‰J�wÓ€EØ�z‰zÐ/Ó0€HØ€JØÐ™Xð	Ü˜X“ˆHØ�8Ô!˜cÕ!Ü"¤5¨£<°1°xÀ#±~Ñ3EÑ#FÈÓJ�
ð —J‘J˜~Ó.Ò4°"×;Ñ;Ó=×CÑCÓE€LØ¤MÐ1€Ià€Ið0ò 6‰ˆ�ð —‘˜FÓ#ˆÜ�fœdÕ#ªØ.4Ö5¨œc !�fÒ5ˆI�cŠNð6ðØ�v—z‘z -Ó0ðà�—‘˜GÓ$ðð 	�v—z‘z -Ó0ðð 	�—‘˜GÓ$ð	ð
 	�$ðð 	�v—z‘z -Ó0ðð 	�V—Z‘Z Ó,ðð 	˜Ÿ
™
 >Ó2ðð 	˜ðð 	�—
‘
˜6Ó"Ò( bðð 	�rðð 	�(ðð 	˜Dðð 	Ø—:‘:˜eÓ$ØØ *Ø"Ø"&Øñ
ð ðð, 	˜"ð-ð. 	�zð/ð0 	�jð1ð2 &Ø—*‘*˜XÓ&ØŸ
™
 >Ó2ØŸ™ OÓ4ò9ð øô œ:Ô'8Ð9ò 	Ø‹Jð	üò 6s   Á7H Ã:H-ÈH*È)H*Úsamplec                 ó°   — t        |«      }|d   | d<   | j                  d«      s!t        | j                  d«      | d   «      | d<   t        t        || «       y)aB  Infer this source's field map from its first record and store it.

    Runs once, on a source's first sync: config["fields"] being populated is
    what tells every sync after this one to skip inference and its model
    call entirely, so the result is written back to the row here rather than
    left to the caller.
    r+   Úurl_templateÚbase_urlN)r   r#   r   r   r   )r   re   r(   Úproposals       r   Ú_infer_and_cache_fieldsrj   v   sW   € ô ! Ó(€HØ Ñ)€Fˆ8ÑØ�:‰:�nÔ%Ü!3Ø�J‰J�zÓ" F¨8Ñ$4ó"6ˆˆ~Ñäœ¨
°FÕ;r   )Úclientrk   c             ƒ   óì  K  — | j                  d«      xs i }|j                  d«      dk7  rt        d|j                  d«      › �«      ‚| j                  d«      }|st        d«      ‚t        |«       t        |j                  dd«      «      }t	        || «      }|d u }|xs t        j                  d	¬
«      }	g d}}
	 t        t        «      D �]F  }|	j                  |||j                  dd«      ||j                  dd«      |i¬«      ƒ d {  –—† }|j                  dk7  r9t        j                  d||j                  «       t        d|j                  › �«      ‚|j                  «       }t        || d   «      xs g }|dk(  r#| j                  d«      s|rt        | |d   |«       |
j                  |«       |j                  d«      rt        ||d   «      nd }|�	 t        |«      }||z  }|r|€�Œ#||k\  s�Œ*|
c |r|	j%                  «       ƒ d {  –—†  S S  t        |› dt        › d�«      ‚7 �Œ # t         t"        f$ r t        d|› d|›�«      ‚w xY w7 ŒC# |r|	j%                  «       ƒ d {  –—†7   w w xY w­w)NÚ
paginationr   Úoffsetzunsupported pagination style: rh   z%source config must declare a base_urlÚ	page_sizeé   g      N@)Útimeoutr   Úlimit_paramÚlimitÚoffset_paramÚskip)ÚheadersÚparamséÈ   z$Catalog fetch for %s failed: HTTP %szHTTP Úrecords_pathr+   Ú
total_pathznon-numeric total from z: z exceeded MAX_PAGES (z) without completing)r#   r   r	   Úintr%   ÚhttpxÚAsyncClientÚrangeÚ	MAX_PAGESÚstatus_codeÚloggerÚerrorÚjsonr   rj   ÚextendrT   rU   Úaclose)r   r   r(   rk   ÚpgÚbaseÚsizerv   ÚownedÚcÚrecordsrn   Ú_ÚrespÚbodyÚpageÚtotals                    r   Úfetch_productsr‘   †   s�  è ø€ ð 
�‰�LÓ	!Ò	' R€BØ	‡v�vˆgƒ˜(Ò"ÜÐ >¸r¿v¹vÀg»Ð>OÐPÓQÐQà�:‰:�jÓ!€DÙÜÐEÓFÐFô
 �DÔäˆr�v‰v�k 2Ó&Ó'€DÜ˜;¨Ó/€Gà�dˆN€EØÒ1”%×#Ñ#¨DÔ1€AØ˜!ˆV€Gð&Ü”yÓ!ó 	ˆAØŸ™˜t¨WØ—‘�} gÓ.°Ø—‘�~ vÓ.°ð>˜ó ÷ ˆDð ×Ñ 3Ò&Ü—‘ÐCØ'¨×)9Ñ)9ô;ä%¨¨d×.>Ñ.>Ð-?Ð&@ÓAÐAà—9‘9“;ˆDÜ  f¨^Ñ&<Ó=ÒCÀˆDà˜Š{ 6§:¡:¨hÔ#7¹DÜ'¨°°Q±¸ÔDà�N‰N˜4Ô à<>¿F¹FÀ<Ô<P”L  r¨,Ñ'7Ô8ÐVZˆEØÐ ðKÜ ›J�Eð
 �d‰NˆFÙ˜EÒ-°&¸E´/Ø‘ñ Ø—(‘(“*×Ñð ðI	ôB ØˆlÐ/´	¨{Ð:NÐOóQð 	QðAúô* "¤:Ð.ò KÜ)Ø1°*°¸RÀ¸yÐIóKð KðKúð ùñ Ø—(‘(“*×Òð üst   ‚B9I4Â<AI Ä
H*ÄCI ÇH-Ç#	I Ç.I Ç5I Ç8I4ÈIÈI4ÈI È-!IÉI ÉI4ÉI1É)I,É*I1É1I4)r   Úloggingr|   Úapp.services.catalog.fieldmapr   r   Ú&app.services.catalog.fieldmap_proposerr   r   Úapp.services.catalog.sourcesr   r   Úapp.services.infra.ssrfr	   Ú	getLoggerr   r�   rY   r   Ú	Exceptionr   Údictr%   r\   Úboolrd   rj   r}   r[   r‘   r   r   r   ú<module>r›      sü   ðñó
 ã ç Gß Xß LÝ 3à	ˆ×	Ñ	˜8Ó	$€ò J€ð
 €	ô6�iô 6ð:˜dð :¨Dð :°Tó :ð& 04ò@˜dð @¨Dð @¸cð @Ø(,ð@Ø8<ó@ðF< Dð <°$ð <ÀCð <ÈDó <ð" @Dò= ð =°Dð =Àcð =Ø$)×$5Ñ$5¸Ñ$<ð=ØHLÈTÉ
ô=r   