
    CoiC                         d Z ddlmZmZ ddlmZ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 dd	lmZmZ dd
lmZmZmZmZmZ ddlmZ  ee          Z  G d d          Z!dS )zy
Data synchronization orchestration service.

Coordinates Garmin API data fetching, storage, and incremental sync logic.
    )date	timedelta)ListDictAnyOptionalTuple)Path)config)GarminHealthClient)HealthStorage)HealthRepository)	SyncErrorGarminAPIError)
parse_date
date_rangesplit_date_rangeget_yesterdayformat_date)setup_loggerc                      e Zd ZdZ	 	 	 ddee         dee         dee         ddfdZddZ		 dd
e
dededefdZ	 dd
e
dedededee
ef         f
dZ	 ddedededee
ef         fdZ	 	 ddededeee
                  dedee
ee
ef         f         f
dZ	 	 ddeee
                  dee         dee
ee
ef         f         fdZddej        fdedee         deee
                  dedee
ee
ef         f         f
dZdee
ef         fdZdS )HealthDataSyncz6Service for orchestrating health data synchronization.Nclientstoragereporeturnc                     |pt                      | _        |pt                      | _        |pt	                      | _        dS )a	  Initialize sync service.

        Args:
            client: Optional Garmin client (creates new if not provided)
            storage: Optional storage service (creates new if not provided)
            repo: Optional repository (creates new if not provided)
        N)r   r   r   r   r   r   )selfr   r   r   s       2/root/projects/butler/health/services/data_sync.py__init__zHealthDataSync.__init__   s>     4 2 4 41-//.,..			    c                     t                               d           | j                                         t                               d           dS )zpAuthenticate with Garmin Connect.

        Raises:
            GarminAuthError: If authentication fails
        zAuthenticating with Garmin...zAuthentication successfulN)loggerinfor   authenticate)r   s    r   r%   zHealthDataSync.authenticate0   sD     	3444  """/00000r!   Fmetric_typetarget_dateforcec           	         |s>| j                             ||          r#t                              d| d| d           dS t	          | j        d| d          }|st                              d|            dS 	  ||          }|s"t                              d| d	|            dS | j                             ||           t                              d
| d|            dS # t          $ r)}t          
                    d| d| d|             d}~wt          $ r.}t          
                    d| d| d|            Y d}~dS d}~ww xY w)a:  Sync a single daily metric for a specific date.

        Args:
            metric_type: Type of metric to sync
            target_date: Date to sync
            force: If True, re-sync even if data already exists

        Returns:
            True if data was synced, False if skipped or no data available
        z	Skipping z for  (already synced)Ffetch_Nz!No fetch method for metric type: zNo z data available for u   ✓ Synced TzAPI error syncing : zError syncing )r   metric_existsr#   debuggetattrr   warningsave_daily_metricr$   r   error	Exception)r   r&   r'   r(   fetch_methoddataes          r   sync_daily_metricz HealthDataSync.sync_daily_metric:   s     	33KMM 	LLU[UU{UUUVVV5 t{,B[,B,BDII 	NNL{LLMMM5	<,,D Q;QQKQQRRRu L**4===KKEkEEEEFFF4 	 	 	LLRkRRRRqRRSSS 	 	 	LLN+NNKNN1NNOOO55555	s*   <-C( +;C( (
E2$DE##EE
start_dateend_datec                    t                               d| d| d| d||z
  j        dz    d	           dddd}t          ||          D ]}	 |                     |||	          r|d
xx         dz  cc<   n|dxx         dz  cc<   =# t
          $ r:}t                               d| d|            |dxx         dz  cc<   Y d}~|d}~ww xY wt                               | d|d
          d|d          d|d          d           |S )a>  Sync a daily metric for a date range.

        Args:
            metric_type: Type of metric to sync
            start_date: Start date (inclusive)
            end_date: End date (inclusive)
            force: If True, re-sync existing data

        Returns:
            Statistics dictionary with sync counts
        Syncing z from  to  (   z days)r   syncedskippederrorsr(   r@   rA   zAPI error on r,   rB   Nz sync complete: 	 synced, 
 skipped,  errors)r#   r$   daysr   r7   r   r2   )r   r&   r8   r9   r(   statscurrent_dater6   s           r   sync_daily_metrics_rangez'HealthDataSync.sync_daily_metrics_rangef   s   $ 	9{ 9 9* 9 9( 9 9J&,q09 9 9	
 	
 	

 a88&z8<< 	 	L
))+|5)QQ *(OOOq(OOOO)$$$)$$$!   @\@@Q@@AAAh1$	 	 ( (X( ().y)9( (X( ( (	
 	
 	
 s   9B
C
0CC
c           	         t                               d| d|            dddd}	 | j                            ||          }t                               dt	          |           d           |D ] }	 |sS| j                            |j                  r4t                               d|j         d           |d	xx         d
z  cc<   Y| j        	                    |           |dxx         d
z  cc<   t                               d|j         d|j
         d|j                    # t          $ r?}t                               d|j         d|            |dxx         d
z  cc<   Y d}~d}~ww xY wnD# t          $ r7}t                               d|            |dxx         d
z  cc<   Y d}~nd}~ww xY wt                               d|d          d|d	          d|d          d           |S )zSync activities for a date range.

        Args:
            start_date: Start date
            end_date: End date
            force: If True, re-sync existing activities

        Returns:
            Statistics dictionary
        zSyncing activities from r<   r   r?   zFound z activitieszSkipping activity r*   rA   r>   r@   u   ✓ Synced activity r=   z) on zError saving activity r,   rB   NzAPI error fetching activities: zActivity sync complete: rD   rE   rF   )r#   r$   r   fetch_activitieslenr   activity_existsactivity_idr.   save_activityactivity_typer   r3   r2   r   )r   r8   r9   r(   rH   
activitiesactivityr6   s           r   sync_activities_rangez$HealthDataSync.sync_activities_range   s    	AzAAxAA	
 	
 	
 a88	!55j(KKJKK=Z===>>>&    !T\%A%A(BV%W%W !X1EXXX   i(((A-(((  L..x888(OOOq(OOOKKIx/C I I$2I I9AI I   
 !   LL!U(:N!U!URS!U!UVVV(OOOq(OOOHHHH%.  	! 	! 	!LL>1>>???(OOOq OOOOOOOO	! 	(X( ().y)9( (X( ( (	
 	
 	
 sP   AE3 4AD&E3 	AD&%E3 &
E/05E*%E3 *E//E3 3
F4=-F//F4metric_typesc           	      :   |s)d t           j                                        D             }nd |D             }t                              dt          |           d| d|            i }|D ]}	 |                     ||||          }|||<   |d         dk    r#| j                            |||d         	           Q# t          $ r3}t          
                    d
| d|            dddd||<   Y d}~d}~ww xY w	 |                     |||          }	|	|d<   |	d         dk    r#| j                            d||	d         	           n=# t          $ r0}t          
                    d|            dddd|d<   Y d}~nd}~ww xY wt          d |                                D                       }
t          d |                                D                       }|dk    rdn	|
dk    rdnd}| j                            d||||
|dk    r| dnd           |S )a_  Sync all (or specified) metrics for a date range.

        Args:
            start_date: Start date
            end_date: End date
            metric_types: Optional list of metric types to sync (defaults to all)
            force: If True, re-sync existing data

        Returns:
            Dictionary mapping metric type to sync statistics
        c                     g | ]
}|d k    |S rR    .0mts     r   
<listcomp>z3HealthDataSync.sync_all_metrics.<locals>.<listcomp>   s-       %% %%%r!   c                     g | ]
}|d k    |S rX   rY   rZ   s     r   r]   z3HealthDataSync.sync_all_metrics.<locals>.<listcomp>   s"    LLL2|9K9KB9K9K9Kr!   r;   z metric types from r<   rC   r@   r   )total_recordszFailed to sync r,   r>   r?   NrR   zFailed to sync activities: c              3   B   K   | ]}|                     d d          V  dS )r@   r   Ngetr[   rs     r   	<genexpr>z2HealthDataSync.sync_all_metrics.<locals>.<genexpr>  0      HH!1551--HHHHHHr!   c              3   B   K   | ]}|                     d d          V  dS )rB   r   Nra   rc   s     r   re   z2HealthDataSync.sync_all_metrics.<locals>.<genexpr>  rf   r!   successpartialfailedall_metricsrF   )	data_typer8   r9   statusrecords_syncederror_message)r   DATA_TYPE_CONFIGkeysr#   r$   rM   rJ   r   update_last_sync_stater3   r2   rT   sumvaluescreate_sync_record)r   r8   r9   rU   r(   resultsr&   rH   r6   activity_statstotal_syncedtotal_errorsrm   s                r   sync_all_metricszHealthDataSync.sync_all_metrics   s   &  	M  16688  LL MLLLLLWs<((WWZWWXWW	
 	
 	
 ' 	 	K55XU 6   (-$ ?Q&&I44#XU8_ 5       A{AAaAABBB23Q'O'O$	M!77
HTY7ZZN$2GL!h'!++	00 (.:R 1     	M 	M 	MLL:q::;;;/0Q!$L$LGL!!!!!!	M
 HHw~~7G7GHHHHHHHw~~7G7GHHHHH*a//<RSCSCSiiYa	$$#!'6BQ6F6F\2222D 	% 	
 	
 	
 s2   /AB==
C:)C55C:>AE 
F&F  F
until_datec                 v   |st                      }|s+t          t          j                                                  }t
                              d| d           i }|D ]}	 | j                            |          }|r|t          d          z   }n8t          t          j                  }t
                              | d|            dddd}t          j                    }|t          d          z
  }	t          ||	          }
||
k    r|dk    r|                     ||
d	
          }n|                     |||
d	
          }|dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   ||k    rt
                              d| d| d           |dk    r|                     ||d
          }n|                     |||d
          }|dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |d         dk    rA|d         dk    r5|d         dk    r)||
k    r#||k     rt
                              | d           |||<   |d         dk    r| j                            ||           # t&          $ r4}t
                              d| d|            dddd||<   Y d}~d}~ww xY w|S )a#  Perform incremental sync for all metrics since last sync.

        Args:
            metric_types: Optional list of metric types to sync
            until_date: Optional end date (defaults to yesterday)

        Returns:
            Dictionary mapping metric type to sync statistics
        z!Starting incremental sync (until )r>   )rG   z" not synced before, starting from r   r?   rR   FrC   r@   rA   rB   zForce syncing z for today (Tz already up to datezIncremental sync failed for r,   N)r   listr   rp   rq   r#   r$   r   get_last_sync_dater   r   HISTORICAL_START_DATEr   todayminrT   rJ   rb   rr   r3   r2   )r   rU   r{   rv   r&   	last_syncr8   rH   r   	yesterdayhistorical_endh_statst_statsr6   s                 r   sync_incrementalzHealthDataSync.sync_incremental$  s     	)&J 	@ 7 < < > >??LE
EEEFFF' @	 @	K? I88EE	  !*YA->->->!>JJ ",F,H!I!IJKK&VV*VV   $%a@@
!I1$5$5$55	 "%Z!;!;//"l22"&"<"<Z_d"<"e"e"&"?"?'^5 #@ # # (OOOw{{8Q'?'??OOO)$$$Iq(A(AA$$$(OOOw{{8Q'?'??OOO &&KK R R R% R R RSSS"l22"&"<"<UEQU"<"V"V"&"?"?'T #@ # # (OOOw{{8Q'?'??OOO)$$$Iq(A(AA$$$(OOOw{{8Q'?'??OOO ?a''E(Oq,@,@U9EUYZEZEZ"^33
U8J8J%H%H%HIII',$ ?Q&&I44[*MMM   NKNN1NNOOO23Q'O'O$
 s   $JK88
L6)L11L6
batch_sizec                    |st                      }|s+t          t          j                                                  }t
                              d| d| d||z
  j        dz    d| d	           t          |||          }t
                              dt          |           d           d	 |D             }t          |d          D ]\  }\  }}	t
                              d
| dt          |           d| d|	            |                     ||	|          }
|
                                D ]\  }}||vr	dddd||<   ||         dxx         |                    dd          z  cc<   ||         dxx         |                    dd          z  cc<   ||         dxx         |                    dd          z  cc<   t
                              d           t
                              d           |                                D ]>\  }}t
                              d| d|d          d|d          d|d          d	           ?|S )aI  Backfill historical data in batches.

        Args:
            start_date: Start date for backfill
            end_date: End date (defaults to yesterday)
            metric_types: Optional list of metric types
            batch_size: Days per batch (default: 30)

        Returns:
            Aggregated sync statistics
        z"Starting historical backfill from r<   r=   r>   z days, batch size: r}   zProcessing z batchesc                     i | ]	}|d d d d
S )r   r?   rY   rZ   s     r   
<dictcomp>z6HealthDataSync.backfill_historical.<locals>.<dictcomp>  s1     9
 9
 9
=?B1a889
 9
 9
r!   u   
📦 Batch /r,   )rU   r   r?   r@   rA   rB   u"   
✅ Historical backfill complete!u   
📊 Final Statistics:z  rD   rE   rF   )r   r~   r   rp   rq   r#   r$   rG   r   rM   	enumeraterz   itemsrb   )r   r8   r9   rU   r   batchesaggregated_resultsibatch_start	batch_endbatch_resultsr&   rH   s                r   backfill_historicalz"HealthDataSync.backfill_historical  s   $  	'$H 	@ 7 < < > >??LS S S S SJ&,q0S SEOS S S	
 	
 	
 #:xDD8#g,,8889999
 9
CO9
 9
 9
 ,5Wa+@+@ 	T 	T'A'YKKPPPCLLPPKPPYPP   !11Y\ 2  M
 '4&9&9&;&; T T"U&888ABq\]6^6^&{3";/999UYYxQR=S=SS999";/	:::eii	ST>U>UU:::";/999UYYxQR=S=SS9999T 	9:::.///"4":":"<"< 	 	KKKH[ H HE(O H H#H H/4XH H H   
 "!r!   c                     i }| j                                         }t          j                                        D ]5}||v r%||         }|d         |d         |d         dd||<   +ddddd||<   6|S )	zzGet current sync status for all data types.

        Returns:
            Dictionary with sync status information
        last_sync_dater_   
updated_atr@   )r   r_   r   rm   Nr   never_synced)r   get_all_last_sync_statesr   rp   rq   )r   rm   
all_statesrl   states        r   get_sync_statuszHealthDataSync.get_sync_status  s     Y7799
05577 	 	IJ&&"9-&+,<&=%*?%;"'"5&	% %y!! '+%&"&,	% %y!! r!   )NNN)r   N)F)NF)NN)__name__
__module____qualname____doc__r   r   r   r   r    r%   strr   boolr7   r   intrJ   rT   r   rz   r   r   DEFAULT_BATCH_SIZE_DAYSr   r   r   rY   r!   r   r   r      s       @@ 04+/+/	/ /+,/ -(/ '(	/
 
/ / / /"1 1 1 1 BG* **-1*:>*	* * * *b , ,, , 	,
 , 
c3h, , , ,^ ?D: ::*.:7;:	c3h: : : :@ -1R RR R tCy)	R
 R 
c4S>!	"R R R Rl -1%)Z ZtCy)Z TNZ 
c4S>!	"	Z Z Z Z~ $(,0 8@" @"@" 4.@" tCy)	@"
 @" 
c4S>!	"@" @" @" @"Dc3h      r!   r   N)"r   datetimer   r   typingr   r   r   r   r	   pathlibr
   healthr   health.services.garmin_clientr   health.services.storager   health.db.repositoryr   health.utils.exceptionsr   r   health.utils.date_utilsr   r   r   r   r   health.utils.logging_configr   r   r#   r   rY   r!   r   <module>r      sj    % $ $ $ $ $ $ $ 3 3 3 3 3 3 3 3 3 3 3 3 3 3             < < < < < < 1 1 1 1 1 1 1 1 1 1 1 1 = = = = = = = =              5 4 4 4 4 4	h		@ @ @ @ @ @ @ @ @ @r!   