o
    HiH>                     @   s   d dl Z d dlZd dlZd dl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 eeZdZdZe Zg aG dd	 d	ZdS )
    N)	timedelta)timezone)MembershipCache)ThreadPoolExecutoras_completed)Lock<   c                   @   sx   e Zd ZdZedZedZedZe	dd Z
e	dd Ze	d	d
 Ze	dddZe	dd Ze	dd ZdS )MembershipAPIServicez8Service for interacting with ICTAZ Membership System APIMEMBERSHIP_API_BASE_URLMEMBERSHIP_API_KEYMEMBERSHIP_API_SECRETc                 C   s2   | j std| jr| jstd| j| jddS )z#Get API headers with authenticationz]Membership API base URL not configured. Set MEMBERSHIP_API_BASE_URL in environment variables.zuMembership API credentials not configured. Set MEMBERSHIP_API_KEY and MEMBERSHIP_API_SECRET in environment variables.zapplication/json)z	X-API-KeyzX-API-SecretzContent-Type)BASE_URL
ValueErrorAPI_KEY
API_SECRETcls r   8/var/www/html/smartRegister/backend/apps/cpd/services.pyget_headers   s   z MembershipAPIService.get_headersc                    s   t M t   fddtD atttkr@td }t |  }|dkr@td|dd t| t   fddtD at	t  W d   dS 1 sRw   Y  dS )	z<Rate limiter: ensures we don't exceed 60 requests per minutec                       g | ]
} | t k r|qS r   RATE_WINDOW.0tsnowr   r   
<listcomp>1       z<MembershipAPIService.wait_for_rate_limit.<locals>.<listcomp>r   zRate limit reached. Waiting z.2fz seconds...c                    r   r   r   r   r   r   r   r   <   r   N)
rate_limiter_locktimerequest_timestampslen
RATE_LIMITr   loggerinfosleepappend)r   oldest	wait_timer   r   r   wait_for_rate_limit)   s   
"z(MembershipAPIService.wait_for_rate_limitc                 C   s  t  tdd }z*tjj|||d}td| d|  |jr1|j	|j
|j|j|j|jdW S W dS  tjyI   td| d| d	 Y nw | j d
}||d}|  }td td|  td|  td|  |   ztj|||ddd}td|j  tdt|j  td|j  td td |  | }	|	dr|	dr|	d }
|
dd|
dd|
dd|
dd|
dd|
ddd}tjj||d |d! |d" |d# |d$ |d |d d%d& |W S tjj||d'did& W dS  tjjyA } z td(| d| d)|  tjj||d'did& W Y d}~dS d}~ww )*a&  
        Lookup member by national ID (NRC or Passport) with caching
        
        Args:
            national_id_type: "NRC" or "Passport"
            national_id_no: National ID number
            
        Returns:
            dict: Member data if found, None if not found or error
           daysnational_id_typenational_id_nolast_lookup__gte   ✓ Cache HIT for  membership_number
first_namemiddle_name	last_nameemailmobileNu   ✗ Cache MISS for z - calling APIz/members/individual/lookup)nationalIdTypenationalIdNoz=== MEMBERSHIP API CALL ===zURL: z	Payload: z	Headers: 
   F)jsonheaderstimeoutverifyzResponse Status: zResponse Headers: zResponse Body: z=== END API CALL === successdatamembershipNo	firstName
middleNamelastNamer:   r;   Tr6   r7   r8   r9   )found_in_systemr6   r7   r8   r9   r:   r;   )r0   r1   defaultsrJ   zError looking up member : )r   r   r   r   objectsgetr%   r&   rJ   r6   r7   r8   r9   r:   r;   DoesNotExistr   r   r+   requestspoststatus_codedictr@   textraise_for_statusr?   update_or_create
exceptionsRequestExceptionerror)r   r0   r1   cache_expirycachedurlpayloadr@   responserE   member_dataresulter   r   r   lookup_memberA   s   	









z"MembershipAPIService.lookup_memberr>   c                    s  g }g }|D ]l}|j s||df q|j  }d|v rdnd}t tdd }z7tjj|||d}	|	j	rM|	j
|	j|	j|	j|	j|	jd}
|||
f n||df td	| d
|  W q tjyr   ||||f Y qw |rtdt| d  fddt|dPfdd|D }t|D ]9}z| }|| W q ty } z || \}}}td|j d|  ||df W Y d}~qd}~ww W d   n1 sw   Y  dd |D }g }|D ]}||j}
 ||
}|| q|S )a>  
        Enrich multiple members in parallel with rate limiting
        
        Args:
            registered_members: List of RegisteredMember instances
            max_workers: Maximum number of parallel threads (default 10)
            
        Returns:
            list: List of enriched member data dicts
        N/NRCPassportr,   r-   r/   r5   r3   r4   zPerforming z parallel API lookups...c                    s   | \}}}  ||}||fS )N)rb   )itemmemberr0   doc_numr_   r   r   r   lookup_single   s   
z?MembershipAPIService.bulk_enrich_members.<locals>.lookup_single)max_workersc                    s   i | ]	}  ||qS r   )submit)r   rf   )executorri   r   r   
<dictcomp>   s    z<MembershipAPIService.bulk_enrich_members.<locals>.<dictcomp>zError enriching member rL   c                 S   s   i | ]\}}|j |qS r   )id)r   mrE   r   r   r   rm     s    )document_numberr(   stripr   r   r   r   rM   rN   rJ   r6   r7   r8   r9   r:   r;   r%   r&   rO   r#   r   r   r`   	ExceptionrY   registration_codern   _build_enriched_dict)r   registered_membersrj   enriched_resultsmembers_to_lookuprg   rh   r0   rZ   r[   r_   futuresfuturer`   ra   _member_to_datafinal_resultsenrichedr   )r   rl   ri   r   bulk_enrich_members   sj   
z(MembershipAPIService.bulk_enrich_membersc           	      C   s  |r<|j r
|j  nd}d|v rdn|rdnd}|dd|||dd|dd|dd|d	d|d
ddd	S |jdd}t|dkrM|d nd}t|dkrY|d nd}t|dkre|d nd}|j pkd}d|v rrdn|rvdnd}|jp|d||||||jpd|jpddd	S )zDHelper to build enriched data dict from member and optional API datarC   rc   rd   re   r6   r7   r8   r9   r:   r;   membership_system	r6   r0   national_id_numberr7   r8   r9   r:   r;   sourcer4      r      attendance_list)	rp   rq   rN   	full_namesplitr#   r6   r:   phone_number)	r   registered_memberr_   rh   r0   full_name_partsr7   r8   r9   r   r   r   rt     s:   






z)MembershipAPIService._build_enriched_dictc           
      C   sf  d}|j r|j  }d|v rd}nd}| ||}|re|j r#|j  nd}d|v r+dn|r/dnd}|dd|||dd|dd|d	d|d
d|dddd	}td|j d|d   |S |jdd}t	|dkrv|d nd}t	|dkr|d nd}t	|dkr|d nd}	|j pd}d|v rdn|rdnd}|j
pd|||||	|jpdddd	S )z
        Enrich registered member data with membership system data
        
        Args:
            registered_member: RegisteredMember instance
            
        Returns:
            dict: Enriched member data
        Nrc   rd   re   rC   r6   r7   r8   r9   r:   r;   r   r   zEnriched data for z	: Mobile=r4   r   r   r   r   r   )rp   rq   rb   rN   r%   r&   rs   r   r   r#   r6   r:   )
r   r   r_   rh   r0   r}   r   r7   r8   r9   r   r   r   enrich_member_data9  sL   







z'MembershipAPIService.enrich_member_dataN)r>   )__name__
__module____qualname____doc__osgetenvr   r   r   classmethodr   r+   rb   r~   rt   r   r   r   r   r   r	      s"    





zS
(r	   )rP   loggingr   r!   datetimer   django.utilsr   apps.cpd.modelsr   concurrent.futuresr   r   	threadingr   	getLoggerr   r%   r$   r   r    r"   r	   r   r   r   r   <module>   s    
