
    BizO                        d Z ddlmZmZmZmZ ddlmZ ddlmZ ddl	m
Z
mZ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Zdd
lZddlmZ  ed          Z ej        e          Zd ZddZddZ d Z!ddZ"ddZ#d Z$ddZ%ddZ&d
S )u   
크레딧 관리 유틸리티
- 크레딧 소모 순서: 이벤트 크레딧 → 일일 크레딧 → 월간 크레딧 → 추가 크레딧 → 무료 크레딧
- 크레딧 만료 정책 처리
    )datetime	timedeltadatetimezone)and_)db)CreditBalanceCreditTransaction
CreditType)SubscriptionSubscriptionStatus)User)resolve_user_ref)mirror_balance_and_transactionsN)ZoneInfoz
Asia/Seoulc           	      6   t           j                            |                                           }|sdt          | dddddd          }t          j                            |           t          j                                         t          | |           |S )u%   크레딧 잔액 조회 또는 생성user_idr   r   total_creditfree_creditevent_creditdaily_renewal_creditsubscription_creditadd_on_credit)balance)	r	   query	filter_byfirstr   sessionaddcommitr   )r   credit_balances     Q/var/www/html/web/mlink/mlink_AI_Server/mlink-backend/src/utils/credit_manager.pyget_or_create_credit_balancer%      s    "(2272CCIIKKN I&!" !
 
 
 	
~&&&

'HHHH    c                 t   	 |r|dk    rddddS |rn|rlt           j                            | t          j        ||                                          }|r+d|j        |j        rt          j	        |j                  ni dS t          |           }|j        |k     rd|j        ddS |}dddddd	}t          ||j                  }	|xj        |	z  c_        ||	z  }|	|d
<   |dk    r/t          ||j                  }	|xj        |	z  c_        ||	z  }|	|d<   |dk    r/t          ||j                  }	|xj        |	z  c_        ||	z  }|	|d<   |dk    r/t          ||j                  }	|xj        |	z  c_        ||	z  }|	|d<   |dk    r/t          ||j                  }	|xj        |	z  c_        ||	z  }|	|d<   |j        |j        z   |j        z   |j        z   |j        z   |_        t          | t          j        | |j        |||t          j        |                    }
t&          j                            |
           t&          j                                         t/          | ||
g           d|j        |dS # t0          $ r;}t&          j                                         ddt5          |          dcY d}~S d}~ww xY w)u  
    크레딧 소모 (소모 순서: 이벤트 → 일일 → 월간 → 추가 → 무료)
    
    Args:
        user_id: 사용자 ID
        amount: 소모할 크레딧 양 (양수)
        description: 거래 설명
        reference_id: 참조 ID
        reference_type: 참조 타입
    
    Returns:
        dict: {'success': bool, 'remaining_balance': int, 'error': str}
    r   Fu!   amount는 양수여야 합니다.)successremaining_balanceerrorr   credit_typereference_typereference_idT)r(   r)   consumed_breakdownu   크레딧이 부족합니다.)eventdaily_renewalsubscriptionadd_onfreer0   r1   r2   r3   r4   r   r,   amountbalance_afterdescriptionr.   r-   	meta_datar   transactionsN)r
   r   r   r   USAGEr   r7   r9   jsonloadsr%   r   minr   r   r   r   r   dumpsr   r    r!   r"   r   	Exceptionrollbackstr)r   r6   r8   r.   r-   dupr#   	remaining	breakdownusetxnes               r$   consume_creditsrJ   *   s   IK 	l1$1Gjkkk  	N 	#)33&,-)	 4  
 egg   #),):GJ}*\$*S]*C*C*CZ\   6g>>&//$>;V  bA  B  B  B	q1TUVV	 )^899##s*##I,<ISVi>P Q;;i!DEEC//36//	S8H	gj)TcJd Q;;i!CDDC..#5..yC7GyehSaIb Q;;i!=>>C((C/((c1AY\9XCV Q;;i!;<<C&&#-&&yC/?yUX6AR &)DD~Gjj./1?1MN 	#
  "(7(5#%)j++	
 	
 	
 	
s

'WZV[\\\\n6Qirsss K K K

 q3q66JJJJJJJJKs0   I2 A/I2  $I2 %GI2 2
J7<0J2,J72J7c           	      f   	 t          |           }|t          j        k    r|xj        |z  c_        n|t          j        k    r|xj        |z  c_        nw|t          j        k    r|xj        |z  c_        nV|t          j        t          j	        t          j
        fv r|xj        |z  c_        n |t          j        k    r|xj        |z  c_        |j        |j        z   |j        z   |j        z   |j        z   |_        t          | |||j        |||          }t           j                            |           t           j                                         t)          | ||g           d|j        dS # t*          $ r;}t           j                                         ddt/          |          dcY d}~S d}~ww xY w)	uZ  
    크레딧 추가
    
    Args:
        user_id: 사용자 ID
        credit_type: CreditType enum
        amount: 추가할 크레딧 양 (양수)
        description: 거래 설명
        reference_id: 참조 ID
        reference_type: 참조 타입
    
    Returns:
        dict: {'success': bool, 'new_balance': int, 'error': str}
    r   r,   r6   r7   r8   r.   r-   r:   T)r(   new_balanceFr   )r(   rM   r*   N)r%   r   EVENTr   FREEr   DAILY_RENEWALr   SUBSCRIPTION_BASICSUBSCRIPTION_PLUSSUBSCRIPTION_PROr   ADD_ONr   r   r
   r   r    r!   r"   r   rA   rB   rC   )	r   r,   r6   r8   r.   r-   r#   transactionrI   s	            r$   add_creditsrV      s   1
5g>> ****''61'''JO++&&&0&&&J444//69///Z:J<XZdZuvvv..&8...J---((F2(( &'(/0 ./ (	) 	# (#(5#%)
 
 
 	
{###

'WbVcdddd )6
 
 	

  
 
 


VV
 
 	
 	
 	
 	
 	
 	

s   E(E+ +
F050F+%F0+F0c           	         	 t          j        t          j                  }|                    t
                    }|                    d          }t          j        	                    | t          j        d|                                          }|rddddS t          j        	                    |                                                                           }|sSt          | dddddd	          }t          j                            |           t          j                                         t'          |j        pd          }d
}|dk    rsd|_        |j        |j        z   dz   |j        z   |j        z   |_        t          | t          j        | |j        dd|          }t          j                            |           d}||_        |j        |j        z   |j        z   |j        z   |j        z   |_        ||_        t          | t          j        ||j        dd|          }	t          j                            |	           t          j                                         d ||	fD             }
t;          | ||
           d|dS # t<          $ r;}t          j                                         ddtA          |          dcY d
}~S d
}~ww xY w)u   
    일일 크레딧 '재설정' (미사용분 만료 후 =300 설정)
    - 멱등: 같은 KST 날짜에 중복 호출되어도 1회만 반영
    z%Y-%m-%ddaily_refillr+   Tr   u$   이미 오늘 갱신되었습니다.)r(   addedmessager   r   Nu   일일 크레딧 만료daily_expire)r   r,   r6   r7   r8   r-   r.   i,  u"   매일 갱신 크레딧(재설정)c                     g | ]}||S N ).0ts     r$   
<listcomp>z'renew_daily_credits.<locals>.<listcomp>  s    EEEaq}}}}r&   r:   )r(   rY   Fr(   rY   r*   )!r   nowr   utc
astimezoneKSTstrftimer
   r   r   r   rP   r   r	   with_for_updater   r    r!   flushintr   r   r   r   r   r   EXPIRElast_daily_renewalr"   r   rA   rB   rC   )r   now_utcnow_kst	today_keyexistsr#   expired
expire_txndaily_amount
refill_txntxnsrI   s               r$   renew_daily_creditsrv      s
   
K?,x|,,$$S))$$Z00	 #(22"0)"	 3 
 

 %'' 	  	d#a<bccc ',66w6GG_uuww 	  	*AA%&AQ  N
 JNN>***J n9>Q??
Q;;23N/*^-HH"679G9UV ' +&-x,95-&  J JNN:&&& .:+&)DD/02@2TU() 	#
 -4)&"0(5<)"
 
 

 	
z"""

EEJ
3EEE'VZ[[[[,777 ? ? ?

 1s1vv>>>>>>>>?s%   BJ G,J 
K0K=KKc           	         	 |r t           j                            |          }nlt           j                            | t          j                                      t           j                                                  	                                }|sddddS ddl
m} |j        r|j                            |j                  nd}|sddddS |j                                        }d	|v rd
}t          j        }n4d|v rd}t          j        }n!d|v rd}t          j        }nd
}t          j        }t'          | |||j         dt)          |j                  d          }|d         |d         r|nd|                    d          dS # t,          $ r;}	t.          j                                         ddt)          |	          dcY d}	~	S d}	~	ww xY w)u  
    구독 크레딧 갱신 (구독 날짜 기준 매달 자동 갱신)
    
    Args:
        user_id: 사용자 ID
        subscription_id: 구독 ID (None이면 활성 구독 조회)
    
    Returns:
        dict: {'success': bool, 'added': int, 'error': str}
    )r   statusFr   u   활성 구독이 없습니다.rb   )ProductNu0   구독 상품 정보를 찾을 수 없습니다.basicil  plusi<  proi8J  u    구독 크레딧 (월간)r2   )r   r,   r6   r8   r.   r-   r(   r*   )r   r   getr   r   ACTIVEorder_by
created_atdescr   src.models.subscriptionry   
product_idnamelowerr   rQ   rR   rS   rV   rC   idrA   r   r    rB   )
r   subscription_idr2   ry   productproduct_namecredit_amountr,   resultrI   s
             r$   renew_subscription_creditsr     sG   B
 	?'-11/BBLL'-77)0 8   h|.335566uuww 
  	 9   	433333@L@Wa'-##L$;<<<]a 	 K   |))++l"" M$7KK|## M$6KKl""!M$5KK !M$7K # "<CCC\_--)
 
 
 i(&,Y&7>]]QZZ((
 
 	
  
 
 


VV
 
 	
 	
 	
 	
 	
 	

s+   BF 5F B7F 
G0G GGc           	      >   	 t          |           }|j        dk    rddddS |j        }d|_        |j        |j        z   |j        z   |j        z   |j        z   |_        t          | t          j	        | |j        d|d          }t          j                            |           t          j                                         t          | ||g           d|d	S # t          $ r;}t          j                                         d
dt#          |          dcY d}~S d}~ww xY w)u   
    이벤트 크레딧 만료 처리
    
    Args:
        user_id: 사용자 ID
        event_id: 이벤트 ID (None이면 모든 이벤트 크레딧 만료)
    
    Returns:
        dict: {'success': bool, 'expired': int, 'error': str}
    r   Tu.   만료할 이벤트 크레딧이 없습니다.)r(   rq   rZ   u   이벤트 크레딧 만료event_expiryrL   r:   )r(   rq   F)r(   rq   r*   N)r%   r   r   r   r   r   r   r
   r   rN   r   r    r!   r"   r   rA   rB   rC   )r   event_idr#   expired_amountrU   rI   s         r$   expire_event_creditsr   j  s   /
5g>>&!++K   (4&'# &'(/0 ./ (	) 	# ("("?(55!)
 
 
 	
{###

'WbVcdddd %
 
 	

  
 
 


VV
 
 	
 	
 	
 	
 	
 	

s#   C B4C 
D!0DDDc           	      <   	 ddl m} t          |           }|sddddS t          |j        d          r|j                                        n$t          j                                                    }|t          d          z   }t          | t          j        d	d
dd          }|                    d          s|S  || dd	|          }t          j                            |           t          j                                         t"                              d|  d|            |S # t&          $ rh}t          j                                         t"                              d|  dt-          |                      ddt-          |          dcY d}~S d}~ww xY w)u   
    회원가입 보너스 크레딧 추가 (이벤트 크레딧 +1000, 가입일로부터 30일 이내 사용)
    
    Returns:
        dict: {'success': bool, 'added': int, 'error': str}
    r   )EventCreditLotFu%   사용자를 찾을 수 없습니다.rb   r      )daysi  u,   회원가입 보너스 (30일 이내 사용)signupsignup_bonus)r   r,   r6   r8   r-   r.   r(   )r   r   amount_remaining
expires_atu8   회원가입 보너스 크레딧 지급 완료: user_id=z, amount=1000, expires_at=u8   회원가입 보너스 크레딧 지급 실패: user_id=z, error=N)src.models.creditr   r   hasattrr   r   r   rc   r   rV   r   rN   r}   r   r    r!   r"   loggerinforA   rB   r*   rC   )r   r   usersignup_dater   r   	event_lotrI   s           r$   add_signup_bonusr     s   *?444444  (( 	d$q;bccc 180P0Pkdo**,,,V^VbVdVdViViVkVk 9"#5#5#55
 "(F#'
 
 
 zz)$$ 	M #N#!!	
 
 
	 	
y!!!

~w~~r|~~ ? ? ?

iPWiiadefagagiijjj 1s1vv>>>>>>>>?s+   D) BD) ;A-D) )
F3AFFF6   구독 취소로 인한 구독 크레딧 사용 완료subscription_cancellationc                 n   	 t          |           }|j        dk    rddddS |j        }d|_        |j        |j        z   |j        z   |j        z   |j        z   |_        t          | t          j	        | |j        |||t          j        d|i                    }t          j                            |           t          j                                         t!          | ||g           d|d|idS # t"          $ r;}t          j                                         d	dt'          |          d
cY d}~S d}~ww xY w)u  
    구독 취소 시 구독 크레딧(subscription_credit)만 사용 완료로 처리
    다른 크레딧(이벤트, 일일, 추가, 무료)은 유지
    
    Args:
        user_id: 사용자 ID
        description: 거래 설명
        reference_id: 참조 ID (구독 ID 등)
        reference_type: 참조 타입
    
    Returns:
        dict: {'success': bool, 'consumed': int, 'error': str}
    r   Tu+   사용할 구독 크레딧이 없습니다.)r(   consumedrZ   r2   r5   r:   )r(   r   r/   F)r(   r   r*   N)r%   r   r   r   r   r   r   r
   r   r<   r=   r@   r   r    r!   r"   r   rA   rB   rC   )r   r8   r.   r-   r#   consumed_amountrU   rI   s           r$   !consume_subscription_credits_onlyr     s   4
5g>> -22H   )< ./* &'(/0 ./ (	) 	# ("(##(5#%)j./!BCC	
 	
 	
 	
{###

'WbVcdddd '#1?"C
 
 	
  
 
 


VV
 
 	
 	
 	
 	
 	
 	

s#   C/ CC/ /
D490D/)D4/D46   구독 취소로 인한 남은 크레딧 사용 완료c                 &    t          | |||          S )u  
    구독 취소 시 남은 크레딧을 모두 사용 완료로 처리 (deprecated - consume_subscription_credits_only 사용 권장)
    
    Args:
        user_id: 사용자 ID
        description: 거래 설명
        reference_id: 참조 ID (구독 ID 등)
        reference_type: 참조 타입
    
    Returns:
        dict: {'success': bool, 'consumed': int, 'error': str}
    )r   )r   r8   r.   r-   s       r$   consume_all_remaining_creditsr      s     -Wk<Q_```r&   )NNr]   )r   Nr   )r   Nr   )'__doc__r   r   r   r   
sqlalchemyr   src.models.dbr   r   r	   r
   r   r   r   r   src.models.userr   src.utils.user_refr   src.utils.credit_mirrorr   r=   loggingzoneinfor   rf   	getLogger__name__r   r%   rJ   rV   rv   r   r   r   r   r   r^   r&   r$   <module>r      s   
 9 8 8 8 8 8 8 8 8 8 8 8             J J J J J J J J J J D D D D D D D D             / / / / / / C C C C C C        h|		8	$	$  &WK WK WK WKt@
 @
 @
 @
FP? P? P?fM
 M
 M
 M
`:
 :
 :
 :
z1? 1? 1?hB
 B
 B
 B
Ja a a a a ar&   