
    Rj%                        d Z ddlmZ ddlmZmZ ddl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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  G d d          Z dS )a'  Top-level orchestrator for the Product Decision Engine.

Workflow:
    Receive product JSON -> extract SKUs -> match freight per SKU
    -> analyze each SKU -> compare SKUs -> product-level analysis
    -> final recommendation -> response dict.

Everything runs in memory; nothing is persisted.
    )annotations)AnyOptional   )	constants)as_listclampfirst_presentsaturating_scoreunwrap_response   )MarketAnalyzer)ProductAnalysisProductAnalyzer)RecommendationEngine)RiskAnalyzer)StoreAnalyzer)StrengthAnalyzer)SkuAnalyzer)SkuDecisionSkuDecisionEnginec                      e Zd ZdZd d
Zd!dZd"dZd#dZed$d            Z	ed%d            Z
ed&d            Zed'd            Zd(dZdS ))DecisionEnginez@Coordinates every analyzer and assembles the final API response.product_jsondictfreight_results
list[dict]profit_marginfloatreturnNonec                   t          |pi           | _        |                     |pg           | _        t	          |          | _        t                      | _        t                      | _	        t                      | _        t                      | _        t                      | _        t!                      | _        t%                      | _        d S N)r   _product_json_index_freight_freight_by_skur   _profit_marginr   _product_analyzerr   _store_analyzerr   _sku_enginer   _market_analyzerr   _risk_analyzerr   _strength_analyzerr   _recommendation_engine)selfr   r   r   s       Cmama_care_api/ali_express_ds/pms/engine/services/decision_engine.py__init__zDecisionEngine.__init__   s     -\-?R@@#22?3HbII#M22!0!2!2,,.. . 0 0*nn"2"4"4&:&<&<###    c           
     D   | j                             | j                  }| j                            | j                  }|                     |          }|j        dk    r|j        nd}|                     ||          }|                     |          }| j                            |||          }| j	                            |||          }| j
                            |||          }	| j                            ||||||	          }
|                     |||||||	|
          S )z:Execute the full workflow and return the response payload.r   N)r(   analyzer$   r)   _popularity_scorescore_analyze_skus
_rank_skusr+   r,   r-   r.   	recommend_build_response)r/   productstorepopularity_scorestore_signalsku_decisionsrankingsmarketriskstrengthrecommendations              r0   runzDecisionEngine.run4   s#   (001CDD$,,T-?@@11'::&+kAoou{{4**+;\JJ??=11&..w}MM"**7E=II*227E=QQ4>>UM64
 
 ##UM8VT8
 
 	
r2   r=   'float | None'store_scorelist[SkuDecision]c                D   |                                  }|                     |          }g }|D ]q}t          t          |dd                    }|                    | j                            || j                            |          | j	        |||                     r|S )N)sku_idskuIdid default)sku_dtofreight_jsonr   r=   rG   max_supplier_price)
_extract_sku_dtos_max_supplier_pricestrr
   appendr*   r4   r&   getr'   )r/   r=   rG   sku_dtosrR   	decisionsrP   rJ   s           r0   r7   zDecisionEngine._analyze_skusK   s    ))++!55h??')	 	 	Gw0ISUVVVWWF ((#!%!5!9!9&!A!A"&"5%5 +'9 )  	 	 	 	 r2   c                ^    t          | j        d          }d t          |          D             S )N)z,ae_item_sku_info_dtos.ae_item_sku_info_d_t_oae_item_sku_info_dtossku_info_dtosskusc                <    g | ]}t          |t                    |S  )
isinstancer   ).0dtos     r0   
<listcomp>z4DecisionEngine._extract_sku_dtos.<locals>.<listcomp>e   s'    KKKZT5J5JKKKKr2   )r
   r$   r   )r/   	containers     r0   rS   z DecisionEngine._extract_sku_dtos_   s;    !?
 
	
 LKwy11KKKKr2   rX   c                ^    t                      fd| D             }t          |d          S )z9Anchor used to compare SKU prices against their siblings.c                D    g | ]}                     |          j        S r_   )r4   supplier_price)ra   rb   analyzers     r0   rc   z6DecisionEngine._max_supplier_price.<locals>.<listcomp>k   s*    KKK3(""3''6KKKr2           rN   )r   max)rX   pricesrh   s     @r0   rT   z"DecisionEngine._max_supplier_priceg   s:     ==KKKK(KKK63''''r2   dict[str, dict]c                    i }| D ]c}t          |t                    st          t          |dd                    }t          |d          }|rt          |t                    r|||<   d|S )N)rK   rJ   rM   rN   )freightJsonrQ   freight)r`   r   rU   r
   )r   indexentryrJ   ro   s        r0   r%   zDecisionEngine._index_freightn   s    !#$ 	( 	(EeT** u.A2NNNOOF#E+UVVG (*Wd33 ( 'fr2   rY   dict[str, Optional[str]]c                      sd dD             S dd fd} |d	            |d
 d           |d            |d            |d            |d d           |d            |d d          dS )zDFind the winner per business dimension. ``None`` when no SKU exists.c                    i | ]}|d S r#   r_   )ra   keys     r0   
<dictcomp>z-DecisionEngine._rank_skus.<locals>.<dictcomp>   s&       #C   r2   )bestOverallSkulowestCostSkuhighestProfitSkuhighestMarginSkuhighestStockSkufastestDeliverySkubestValueSkulowestShippingCostSkuTreverseboolr    rU   c                L    t          | |          d         }|j        j        S )N)ru   r   r   )sortedskurJ   )key_fnr   bestrY   s      r0   winnerz)DecisionEngine._rank_skus.<locals>.winner   s&    )AAA!DD8?"r2   c                    | j         j        S r#   resultr6   ds    r0   <lambda>z+DecisionEngine._rank_skus.<locals>.<lambda>   s
    qx~ r2   c                    | j         j        S r#   )pricing
total_costr   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s    ai.B r2   F)r   c                    | j         j        S r#   )r   estimated_profitr   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s    1K r2   c                    | j         j        S r#   )r   marginr   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s    1A r2   c                    | j         j        S r#   )r   stockr   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s
     r2   c                6    | j         j        | j         j        ndS )Ng     8@)ro   delivery_days_avgr   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s"    9.: )55@E r2   c                X    | j         j        dk    r| j        j        | j         j        z  ndS )Nr   ri   )r   r   r   r6   r   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s0    9'!++ (.19+???14 r2   c                    | j         j        S r#   )ro   shipping_costr   s    r0   r   z+DecisionEngine._rank_skus.<locals>.<lambda>   s    !)1 r2   )T)r   r   r    rU   r_   )rY   r   s   ` r0   r8   zDecisionEngine._rank_skus}   s     	  *    	# 	# 	# 	# 	# 	# 	#
 %f%=%=>>#V$B$BERRR &'K'K L L &'A'A B B%v&;&;<<"(&F F# # # #F5 5  &,V115& & &!
 
 	
r2   r;   r   c                    | j         dk    r| j        dk    rdS t          t          | j         t          j                  dz  t          | j        t          j                  dz  z             S )a%  Product-level popularity shared by every SKU: sales + reviews.

        Uses a saturating (square-root) curve so moderate history earns a
        moderate score. Returns ``None`` when neither metric is available,
        letting downstream weights renormalize instead of scoring zero.
        r   Ng333333?g?)sales_countreview_countr	   r   CSALES_FOR_FULL_POPULARITYREVIEWS_FOR_FULL_POPULARITY)r;   s    r0   r5   z DecisionEngine._popularity_score   sv     !##(<(A(A4W0!2MNNQTTw3 <> >@CDD
 
 	
r2   r?   r@   c	                z   |rt          |d           nd }	i d|j        dt          |j                  dt          |j                  d|	r|	j        j        ddnd d	|d
|                                d|	r|	j                                        nd d|                                d|                                dt          |j        d          |j	        o"d |j	        
                                D             ddd |D             d|j        d|j        d|j        d|j        d|j        d|j        S )Nc                    | j         j        S r#   r   r   s    r0   r   z0DecisionEngine._build_response.<locals>.<lambda>   s
    QX^ r2   )ru   decision
confidenceoverallScorebestSkuzHighest overall score)rK   reasonr@   rA   r   r<   r;   scoresr   c                6    i | ]\  }}|t          |d           S )r   )round)ra   kvs      r0   rv   z2DecisionEngine._build_response.<locals>.<dictcomp>   s<     I I IA a I I Ir2   )overall	breakdownskuAnalysisc                6    g | ]}|                                 S r_   )to_dict)ra   r   s     r0   rc   z2DecisionEngine._build_response.<locals>.<listcomp>   s     ???AAIIKK???r2   	strengthsriskssummaryconfidenceReasonsmarketRecommendationskuRecommendation)rj   r   r   r   overall_scorer   rJ   r   r   score_breakdownitemsr   r   r   confidence_reasonsmarket_recommendationsku_recommendation)
r/   r;   r<   r?   r@   rA   rB   rC   rD   r   s
             r0   r:   zDecisionEngine._build_response   s    'C#;#;<<<<"& 	
/
% 9::
 E.">??
 "$(/5LMMM!
 
 fnn&&
 ?t|++---4
 U]]__
 w((
  !=qAA+; II I!/!?!E!E!G!GI I I 
& ?????'
( +)
* TZ+
, ~--
.  !B/
0 #N$H1
2  !B3
 	
r2   N)r   r   r   r   r   r   r    r!   )r    r   )r=   rF   rG   rF   r    rH   )r    r   )rX   r   r    r   )r   r   r    rl   )rY   rH   r    rr   )r;   r   r    rF   )r?   rH   r@   r   r    r   )__name__
__module____qualname____doc__r1   rE   r7   rS   staticmethodrT   r%   r8   r5   r:   r_   r2   r0   r   r      s       JJ= = = =*
 
 
 
.   (L L L L ( ( ( \( 	 	 	 \	  
  
  
 \ 
J 
 
 
 \
$)
 )
 )
 )
 )
 )
r2   r   N)!r   
__future__r   typingr   r   r   r   utils.helpersr   r	   r
   r   r   analyzers.market_analyzerr   analyzers.product_analyzerr   r   analyzers.recommendation_enginer   analyzers.risk_analyzerr   analyzers.store_analyzerr   analyzers.strength_analyzerr   analyzers.sku.sku_analyzerr   !analyzers.sku.sku_decision_enginer   r   r   r_   r2   r0   <module>r      s{    # " " " " "                 & & & & & &@ @ @ @ @ @ @ @ @ @ @ @ @ @ 6 5 5 5 5 5 H H H H H H H H A A A A A A 1 1 1 1 1 1 3 3 3 3 3 3 9 9 9 9 9 9 3 3 3 3 3 3 M M M M M M M MC
 C
 C
 C
 C
 C
 C
 C
 C
 C
r2   