o
    ɣg!                     @   s  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 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 d dlmZ d dlmZ d dlmZ d dlm Z m!Z! e Z"e"j#dedeeeefdedefddZ$e"j#dee deeeefdedefddZ%e"j#dee deefdefddZ&e"j#dee deefdefddZ'e"j#de(deeeefdedefdd Z)e"j*d!e(deeeefdedefd"d#Z+G d$d% d%eZ,e"j*d&e(deeeefd'e,dedefd(d)Z-d*S )+    )	APIRouterDependsHTTPExceptionstatus)Session)List)datetime	timedeltadate)	BaseModel)func)
UserPoints)UserPointsResponse)get_dbget_current_user)User)PointTransaction)PointTransactionResponse)AggregatedPointsResponse)calculate_unconverted_pointsconvert_steps_to_points/)response_modelcurrent_userdbc                    sP   | ttj| jk }|s&t| jdd}|| |  || |S )u   
    로그인한 사용자의 포인트를 조회합니다.
    만약 사용자에 대한 포인트 레코드가 없으면, 0으로 초기화합니다.
    r   user_idtotal_points)	queryr   filterr   idfirstaddcommitrefresh)r   r   user_points r&   #/app/app/api/v1/endpoints/points.pyget_user_points   s   	

r(   z/transactionc                    s.   | ttj| jktj  }|S )u   
    현재 로그인한 사용자의 포인트 트랜잭션 내역을 반환합니다.
    반환되는 항목은 transaction_type, amount, description (그리고 필요시 created_at) 입니다.
    )	r   r   r   r   r    order_by
created_atdescall)r   r   transactionsr&   r&   r'   get_point_transactions&   s   
r.   z/weeklyc                    s   t  }t |j|j|jt| d }| tj	t
t
tjddtj|ktj	t
t
tjddk }dd |D S )u   
    모든 유저의 주간 누적 포인트를 조회합니다.
    현재 주(월요일부터 시작) 이후에 발생한 포인트 트랜잭션을 집계하여
    각 유저별 주간 누적 포인트를 반환합니다.
    )daysr   r   c                 S      g | ]	\}}||d qS r   r&   .0r   r   r&   r&   r'   
<listcomp>P       z%get_weekly_points.<locals>.<listcomp>)r   utcnowyearmonthdayr	   weekdayr   r   r   r   coalescesumamountlabelr   r*   group_byhavingr,   )r   now_dtZstart_of_weekZweekly_pointsr&   r&   r'   get_weekly_points7   s   	 rB   z/monthlyc                    s~   t  }t |j|jd}| tjtt	tj
ddtj|ktjtt	tj
ddk }dd |D S )u   
    모든 유저의 월간 누적 포인트를 조회합니다.
    이번 달의 초(1일) 이후에 발생한 포인트 트랜잭션을 집계하여
    각 유저별 월간 누적 포인트를 반환합니다.
       r   r   c                 S   r0   r1   r&   r2   r&   r&   r'   r4   k   r5   z&get_monthly_points.<locals>.<listcomp>)r   r6   r7   r8   r   r   r   r   r;   r<   r=   r>   r   r*   r?   r@   r,   )r   rA   Zstart_of_monthZmonthly_pointsr&   r&   r'   get_monthly_pointsR   s   	rD   z/unconvertedc                    s   t || j| jS )u   
    아직 포인트로 전환되지 않은 걸음수를 조회합니다.
    마지막 포인트 전환 이후의 걸음 기록에서 얻을 수 있는 잠재적 포인트와
    100점 단위로 실제 전환 가능한 포인트를 계산합니다.
    )r   r    country_code)r   r   r&   r&   r'   get_unconverted_pointsm   s   
rF   z/convertc                    s<   t || j| j}|d sd|d v rttj|d d|S )ut  
    걸음수를 포인트로 전환합니다.
    
    - 마지막 포인트 전환 이후의 모든 걸음 기록을 확인합니다.
    - 전환 가능한 포인트가 1000점 이상인 경우에만 포인트를 적립합니다.
    - 포인트는 1000점 단위로만 전환됩니다 (예: 1950걸음 → 1000포인트만 전환, 950걸음은 다음 전환까지 유지).
    - 전환된 포인트는 "step_conversion" 타입의 트랜잭션으로 기록됩니다.
    - 실시간 걸음 기록은 계속되지만, 자동으로 포인트로 전환되지 않고 반드시 이 엔드포인트를 호출해야 합니다.
    successzMinimum 1000 points requiredmessagestatus_codedetail)r   r    rE   r   r   HTTP_400_BAD_REQUEST)r   r   resultr&   r&   r'    convert_steps_to_points_endpointy   s   rN   c                   @   s   e Zd ZU eed< dS )PointGameRequestresult_pointN)__name__
__module____qualname__int__annotations__r&   r&   r&   r'   rO      s   
 rO   z/point-game	game_datac           	         s$  | j dkrttjddt }t|tj	 }t|tj
	 }|ttj|jktjdktj|ktj|k }|rFttjddt|jd| j d| j  dd}|| |ttj|jk }|rs| j| j 7  _nt|j| j d	}|| |  d
d| j  d| j |jdS )uD  
    포인트 게임을 통해 획득한 포인트를 적립합니다.
    
    - 하루에 한 번만 게임 포인트를 적립할 수 있습니다.
    - 적립된 포인트는 사용자의 총 포인트에 더해집니다.
    - 포인트 적립 내역은 "point_game" 타입의 트랜잭션으로 기록됩니다.
    r   zPoint value must be positiverI   
point_gamez,You have already played the point game todayzGet the event z points)r   transaction_typer=   descriptionr   TzSuccessfully added z points from the point game.)rG   rH   Zadded_pointscurrent_total_points)rP   r   r   rL   r
   todayr   combinemintimemaxr   r   r   r   r    rX   r*   r!   r"   r   r   r#   )	rV   r   r   r[   Ztoday_startZ	today_endZexisting_gamepoint_txr%   r&   r&   r'   point_game_endpoint   sP   




ra   N).fastapir   r   r   r   sqlalchemy.ormr   typingr   r   r	   r
   pydanticr   
sqlalchemyr   Zapp.models.user_pointsr   Zapp.schemas.user_pointsr   Zapp.api.depsr   r   app.models.userr   Zapp.models.point_transactionr   Zapp.schemas.point_transactionr   Zapp.schemas.aggregated_pointsr   Zapp.services.point_servicer   r   routergetr(   r.   rB   rD   dictrF   postrN   rO   ra   r&   r&   r&   r'   <module>   s    