
    itJ                         S r SSKrSSKrSSKrSSKrSSKJr  SSKJr  \R                  " S5      r	Sr
Sr0 rS rS	 rS
 rS rSS jrS rS rSS jrS rS rg)u  
Pre-Game Validator Module for Luke.

Validates betting picks against pregame intelligence before they reach
Bill's dashboard or WhatsApp.

Validation Pipeline:
  1. Tempo filter produces a pick with 3 legs and a rating (existing flow)
  2. Validator enhances with Claude API (primary) + web scraping (supplementary)
  3. Each pick gets a confidence tag:
     ★ VALIDATED  — pregame intel confirms the tempo read
     ⚠ CAUTION   — mixed signals, proceed with care
     ❌ DROPPED   — pregame intel contradicts the tempo read
  4. VALIDATED and CAUTION show prominently; DROPPED are collapsed

Architecture:
  BASELINE:  Tempo classification data already computed (always available)
  PRIMARY:   Claude API (Haiku) — grounded by live RSS headlines
  RSS:       ESPN, TSN, Sportsnet, BBC, Sky Sports, Guardian, Goal.com
  FALLBACK:  Heuristic rules using baseline tempo data if Claude is down
    N)datetime)Configzpregame-validatori      c                     U R                  5       R                  5        SUR                  5       R                  5        3$ )N_)lowerstrip)homeaways     8/Users/bsyrros/clawd/whatsapp-agent/pregame_validator.py
_cache_keyr   *   s3    jjl  "#1TZZ\%7%7%9$:;;    c                     [        X5      n[        R                  U5      nU(       a6  [        R                  " 5       US   -
  R                  5       [        :  a  US   $ g )N	timestampdata)r   _validation_cachegetr   nowtotal_secondsCACHE_TTL_SECONDS)r
   r   keyentrys       r   _get_cachedr   .   sM    
T
 C!!#&E(,,.5#55DDFIZZV}r   c                 X    [        X5      nU[        R                  " 5       S.[        U'   g )N)r   r   )r   r   r   r   )r
   r   r   r   s       r   _set_cachedr   6   s"    
T
 C&*Hcr   c                     S H)  nU R                  U5      (       d  M  U [        U5      S  n M+     S H*  nU R                  U5      (       d  M  U S [        U5      *  n M,     U R                  5       $ )N)zFC zAC zAS zSS zSSC zAFC )z FCz SCz CF)
startswithlenendswithr	   )nameprefixsuffixs      r   _team_shortr#   =   sf    >??6""F%D ? (==  #f+&D ( ::<r   c                 0    SSK JnJnJn  U" 5       nUS:X  a  UR                  X5      nUR                  U 0 5      n	UR                  U0 5      n
U	R                  SS5      nU
R                  SS5      nUS:X  a  SS	S
U SU S3SSSS.$ US:X  aM  U	R                  S5      S:X  a  U OUnUR                  U0 5      R                  SS5      nSSU SU S3U S3SSS.$ SSSSSSS.$ SU0nUR                  U5      nUR                  SS5      nUR                  SUR                  5       5      nUR                  S S5      nUS!:X  a'  U S"3nU(       a  U S#3nSS	US$U(       a  S%OS& S'3SSS.$ US:X  a  SSU S(3S)SSS.$ SSU S*3S+SSS.$ ! [         a     gf = f),zv
Generate a baseline validation using the tempo classification
that sports_betting.py already computed for this game.
r   )NHL_TEAM_PROFILESSOCCER_LEAGUE_TIERSSportsBettingAnalystNnhllabelunknown
controlled	VALIDATED   zBoth teams are low-event (z + )zControlled tempo matchuplowTverdict
confidencereason
key_factorinjury_impactform_supports_undermixedtempo	defensiveCAUTION    (z0) anchors the pace, but risk from the other sidez controls tempoDROPPED   u-   Both teams are high-event — chaotic matchupzHigh-offense tempo profileFsporttieris_cup   u    — prime under territoryu!    knockout — teams protect leadszTier 1 cupleaguez environmentu.    — usable with filtering, check team qualityz-Tier 2 league needs careful matchup selectionu&    — high-scoring league, avoid undersz$Tier 3 league, goals come in bunches)	sports_bettingr%   r&   r'   ImportErrorclassify_nhl_matchupr   classify_soccer_matchupupper)r
   r   r?   	pick_datar%   r&   r'   analystr8   home_paway_p
home_label
away_labelanchoranchor_labelgameleague_classr@   r)   rA   r3   s                        r   _baseline_from_temporT   K   sI   
	
 	
 '( ~,,T8"&&tR0"&&tR0ZZ3
ZZ3
L &6zl#j\QRS8!&'+  g#ZZ0E9TtF,00<@@+VL$#HB|n4de!'8!&'+  %I:!&',  66t<*  %++-8!!(E219w89F!7"CD&  'X'FlS!&'+  QY$"G#QRM!&'+  %"G#IJD!&', M  s   F 
FFc           
      z    SSK JnJn  U" XUSS9nU" USS9n/ nU(       a  UR                  S5        U H  nUR	                  SS5      n	UR	                  S	S5      n
UR	                  S
S5      nUR	                  SS5      SS nU	(       a  SU	R                  5        S3OSnUR                  SU SU 35        U(       a#  [        U5      S:  a  UR                  SU 35        UR                  SU
 35        M     U(       aT  UR                  S5        USS  H:  nUR                  SUR	                  S
S5       SUR	                  S	S5       35        M<     U(       a  SR                  U5      $ S$ ! [         a    [        R                  S5         g[         a"  n[        R                  SU 35         SnAgSnAff = f)z
Pull live headlines from pregame_chatter.py RSS feeds for both teams.
Returns a formatted string of current news, or empty string.
r   )get_pregame_chatterget_general_sport_headlines   )	max_itemsr;   z,TEAM-SPECIFIC NEWS (from today's RSS feeds):	relevance sourcetitlesummaryNx   []      z    u       — z
GENERAL SPORT HEADLINES:       — 
z/pregame_chatter not available for RSS headlinesz*RSS headline fetch failed (non-critical): )pregame_chatterrV   rW   appendr   rI   r   joinrF   loggerdebug	Exception)r
   r   r?   rV   rW   chattergeneralpartsitemrelr\   r]   r^   team_tages                  r   _fetch_live_headlinesru      s   
$T &d%1E .eqALLGHhh{B/(B/"-((9b1$3714Qsyy{m1-"r(1UG45s7|b0LL4y!12xx01   LL56r$((7B"7!8dhhxQS>T=UVW $ $)tyy0b0 FG A!EFs$   E)E. ,E. .F:	F:F55F:c                    [         R                  nU(       d  gUS:X  a  SOSnUS:X  a  SOSnSSS	S
SSSSSSS.
nUR                  X"R                  5       5      n[	        XU5      n	Sn
U	(       a  SU	-   n
[
        R                  " 5       R                  S5      nSU SU SU  SU SU SUR                  SS5       SUR                  SS5       SU
 S3n [        R                  " SUS S!S".S#S$S%US&./S'.[        S(9nUR                  S$:X  au  UR                  5       S)   S*   S+   R                  5       n [        R                  " U5      nSU;   a0  S/U;   a*  [(        R-                  S0U S1U  S2US    SUS/    S33	5        U$ g[(        R+                  S4UR                   35         g! [        R                   aq    [         R"                  " S,U[         R$                  5      nU(       a&  [        R                  " UR'                  5       5      n N[(        R+                  S-USS.  35         gf = f! [.         a"  n[(        R+                  S5U 35         SnAgSnAff = f)6z
Use Claude Haiku to enhance the baseline validation with deeper analysis.
Grounded by LIVE RSS headlines so Claude doesn't hallucinate stale rosters.
Returns enhanced validation dict, or None if Claude is unavailable.
Nr(   NHLsoccerzUnder 6.5 goalszUnder 2.5 goalszSerie AzLigue 1zLiga PortugalEPLzLa LigaMLSzEuropa LeaguezChampions LeaguezConference League)
seriealigue1liga_portugalepllaligamlseuropaucl
conferencer(   r[   uG   

LIVE DATA (from today's feeds — use THIS, not your training data):
z	%B %d, %Yz1You are a sharp sports betting analyst. Today is z.

MATCHUP:  @ r<   z)
BET: uv   
STRATEGY: "Predictable Tempo" — we bet on controlled, low-scoring game environments.

Our tempo filter rated this: r1   UNKNOWNr3   r.   u  

CRITICAL RULES:
- ONLY reference players, injuries, and roster info that appears in the LIVE DATA above
- If no live data mentions a player, do NOT name them — your training data may be outdated (trades, injuries)
- Focus on team-level factors: defensive structure, scoring trends, tactical matchup
- If you have no live data, base your analysis on the tempo profile and general team style

Respond in EXACT JSON only (no markdown, no commentary):
{"verdict":"VALIDATED" or "CAUTION" or "DROPPED","confidence":1-10,"reason":"Max 80 chars","key_factor":"Max 60 chars","injury_impact":"low" or "medium" or "high","form_supports_under":true or false}z%https://api.anthropic.com/v1/messagesz
2023-06-01zapplication/json)z	x-api-keyzanthropic-versionzcontent-typezclaude-haiku-4-5-20251001   user)rolecontent)model
max_tokensmessages)headersjsontimeoutr   r   textz	\{[^}]+\}zClaude non-JSON: d   r2   zClaude: @u    → /10)zClaude API zClaude error: )r   CLAUDE_API_KEYr   rI   ru   r   r   strftimerequestspostCLAUDE_TIMEOUTstatus_coder   r	   loadsJSONDecodeErrorresearchDOTALLgrouprk   warninginform   )r
   r   r?   baselineapi_keysport_label
under_line
league_maprD   live_headlineslive_sectiontodaypromptrespr   resultmrt   s                     r   _validate_with_clauder      s    ##G E>%xK&+un":KJ y?	%!*<)%	J ^^E;;=1F +4u=N LcfttLLN##K0EB5' J

s4&6( #\  'll9i@AHLLQY[]D^C__`am`n 	oJMF"#-}}3$%1 2 5!&,@A
 #
 s"iik),Q/7==?G G, F"|v'=htfAdV5	9J8K2fUaNbMccghi  NN[)9)9(:;<
 # ''  IIlGRYY?!ZZ	2FNN%6wt}o#FG   -s+,,-sP   AH+ 2F# 5H+ ?"H+ #A%H(H+ 
H(%H+ 'H((H+ +
I5IIc           	      x   [        X5      nU(       a  U$ [        R                  SU SU  SU S35        / n[        XX#5      nU(       d	  SSSSS	S
S.nUR	                  S5        [        XX&5      nU(       a  UR                  SS5      nUR                  SS5      n	UR                  SS5      n
US:X  a+  U	S:X  a%  U
S::  a  UnUS    S3US'   UR	                  S5        O9US:X  a   U	S:w  a  U
S::  a  UnUR	                  S5        OUnUR	                  S5        UR	                  S5        OUnX[S'   [        XU5        U$ )a  
Full validation pipeline. Always returns a meaningful result.

1. Check cache
2. Generate baseline from tempo classification (always works)
3. Try web scraping (best-effort, 5s timeout)
4. Try Claude API enhancement (primary, 15s timeout)
5. Return best available result
zValidating: r   r<   r.   r:   r;   z-Tempo profile supports controlled environmentzPassed tempo filterr*   Nr0   Tempo Profiler1   r2   r,   r3   z (Claude: mild caution)z Claude AI (deferred to baseline)r=   z	Claude AIzRSS Headlinessources)r   rk   r   rT   ri   r   r   r   )r
   r   r?   rJ   cachedr   r   claude_resultbaseline_verdictclaude_verdictclaude_confanalysiss               r   validate_pickr   0  s_    $F
KK,tfCvRwa89G $DAH E/&#'
 NN?# *$eFM#<<	9=&**9i@#''a8{*~/J{^_O_H$,X$6#77N!OHXNN=>*~/J{^_O_HNN=> %HNN;''!YH%Or   c                 v  ^ [         R                  " 5       nU  GH  nUR                  SS5      nU(       a   [         R                  " UR	                  SS5      5      R	                  SS9nXA:  aN  SSS	S
/ S.US'   SUS'   [
        R                  SUR                  SS5       SUR                  SS5       S35        M   UR                  SS5      nUS:X  a  SSSS/ S.US'   M  UR                  SS5      nUR                  SS5      nUR                  SS5      R                  5       nSSSSSSSSS S!S"S#.n	U	R                  X5      n
 [        XgX5      nXS'   US$   S:X  a)  US%;   a#  SUS'   S&UR                  S'S5      SS(  3US)'   GMp  US$   S*:X  aB  US+:X  a9  UR                  S,S5      S-:  a   S.US'   S/UR                  S)S5       3US)'   GM  GM  GM  GM     U  Vs/ s H+  oR                  S0 5      R                  S85      S
:w  d  M)  UPM-     n n[        U 5      nU  Vs/ s H-  nUR                  S0 5      R                  S,S5      S-:  d  M+  UPM/     n nSS9K Jn  UR                  5       R!                  5       nU  Vs/ s HO  nUR                  S5      (       a4  UR                  SS5      SS: U:X  d  UR                  S;S5      SS: U:X  d  MM  UPMQ     n nSS<S=S>S?.mU R#                  U4S@ jSA9  [%        SB U  5       5      n[%        SC U  5       5      n[%        SD U  5       5      n[
        R'                  SEU SFU SG[        U 5       SHU SI3	5        U $ ! [        [        4 a     GNf = f! [         a5  n[
        R                  S0U S1U S2U 35        S3S4S5S6/S7.US'    SnAGM  SnAff = fs  snf s  snf s  snf )Jzl
Validate a batch of picks. Adds 'validation' key to each pick dict.
Skips games that have already started.
commence_timer[   Zz+00:00N)tzinfor=   r   u!   Game already started — skippingIn progress)r1   r2   r3   r4   r   
validationavoidratingz	Skipping r   r   r
   u    — already startedneutralzTempo filter flagged as avoidzChaotic tempor?   r(   r~   r{   r|   r   r}   r   
bundesligar   r   r   )r(   r~   zserie azligue 1zla ligazliga portugalr   r   zeuropa leaguezconference leaguezchampions leaguer1   )primeplayableu   ❌ DROPPED — r3   2   tagr,   r   r2   r-   r   u   ★ VALIDATED — zValidation error z vs z: r:   r;   z Tempo profile supports this pickr   )r1   r2   r3   r   r4   )date
   timerB   r>   re   )r   r   r   r   c                    > TR                  U R                  SS5      S5      U R                  S0 5      R                  SS5      * 4$ )Nr   r   r>   r   r2   r   r   )prating_orders    r   <lambda>&validate_picks_batch.<locals>.<lambda>  sA    x3Q7
%%b
!
%
%lA
67r   )r   c              3   r   #    U  H-  oR                  S 0 5      R                  S5      S:X  d  M)  Sv   M/     g7f)r   r1   r,   rB   Nr   .0r   s     r   	<genexpr>'validate_picks_batch.<locals>.<genexpr>  s.     \UeeL"&=&A&A)&LP[&[!!U   (7	7c              3   r   #    U  H-  oR                  S 0 5      R                  S5      S:X  d  M)  Sv   M/     g7f)r   r1   r=   rB   Nr   r   s     r   r   r     s.     ZUeeL"&=&A&A)&LPY&Y!!Ur   c              3   r   #    U  H-  oR                  S 0 5      R                  S5      S:X  d  M)  Sv   M/     g7f)r   r4   r   rB   Nr   r   s     r   r   r     s.     aUeeL"&=&A&A,&OS`&`!!Ur   zValidated: u    ★, u    ❌ | Showing /z (7/10+ confidence, today only))r   utcnowr   fromisoformatreplacerk   rl   
ValueError	TypeErrorr   r   rm   r   r   r   r   	isoformatsortsumr   )picksr   pickcommence	game_timer   r
   r   r?   sport_key_map	sport_keyr   rt   r   
pre_filter_date	today_strv_countd_countstartedr   s                       @r   validate_picks_batchr   l  s   
 //
C88OR0$2283C3CC3RS[[cg[h	?#,&'"E&3#%*D& &-DNLL9TXXfb-A,B#dhhvWYFZE[[o!pq # (I.W$9-"D xx#xx#"%++- 8H,U&%L %
 "%%e3		&t9CJ!+)$	1f@U6U!(X 0"1Mcr1R0STUI&+5&J:N>>,2a7%,DN$6txxr7J6K"LDK 8 ;O5i F \1|R!8!<!<\!Jm![QE\ UJa55r"&&|Q71< 	
5 
  ''')Iauu_%%55"%cr*i755Sb!Y. 	
5 
  A!aHL	JJ J 
 \U\\GZUZZGaUaaG
KK+gYfWI_SZLPQR\Q]]|}~Lc 	* P  	NN.tfDbDE$<+,	"D	 ]sV   BM?M*	AM*(N,	N, *N1N1>AN6N6M'&M'*
N)4)N$$N)c                 t   U R                  S0 5      nUR                  SS5      nUR                  SS5      nUR                  SS5      nUR                  S/ 5      nS	S
SS.nUR                  US5      nSU SU 3nU(       a  USU 3-  nU(       a  USSR                  U5       3-  nU(       a	  USU S3-  nU$ )Nr   r1   r   r3   r[   r2   r   r   u   ★u   ⚠️u   ❌)r,   r:   r=   ?rb   z
 PREGAME: rf   z
    Sources: z, r<   r   )r   rj   )	r   vr1   r3   r2   r   iconsiconlines	            r   format_validation_for_whatsappr     s    r"AeeIy)GUU8R F|Q'JeeIr"GHGE99Wc"DvZy)D%x  /$))G"4!566"ZL%%Kr   )N)__doc__r   r   loggingr   r   configr   	getLoggerrk   r   r   r   r   r   r   r#   rT   ru   r   r   r   r    r   r   <module>r      s   , 
     			.	/    <IXz)\Ut9xi\r   