a
    i]                     @   s0  d Z ddlmZmZmZmZ ddlmZmZ ddlZddl	Z	ddl
Z
ddddd	d
ddddddd	dddddddd	dddddddd	dddddddddd dd!d"dd#ddd$dd%d&dddd'd(dd)d*d+ddd,d-dd.d/d0d'd1d0d2dd3d4d5d6d1d7d8dd9d:d#d;d1d<d=dgZd>d? Zeeeeef  d@dAdBZdheeeeeeef  dCdDdEZeeeedFdGdHZeeeeef  d@dIdJZeeeef d@dKdLZeeef eeef dMdNdOZeeeeef dPdQdRZedSdTdUZdVdW ZdXdY ZdZd[ Zd\d] Zd^d_ Zd`da Zdbdc Zddde Zdfdg Z dS )iz
Daily Quest System

Generates and tracks daily quests for player engagement.
Players receive 3 quests daily (easy/medium/hard) that award diamonds on completion.
    )DictAnyListOptional)datedatetimeNtalk_to_characterszTalk to 3 different characters   
   easy	   message)typedescrequiredreward
difficultyenergyiconbuy_itemzBuy an item from the store      Zcartattend_classzAttend 2 classes         book	socializezSocialize with friendszperson.2
work_hourszWork for 6 hours   medium   	briefcase
go_on_datezGo on a date   heartcomplete_activitieszComplete 5 activities   zcheckmark.circlestudyzStudy for 4 hours      z	book.fillspend_energyzSpend 50 energy on activities2   hardZbolt
earn_moneyz	Earn $500i     (   Z
dollarsignincrease_affinityz$Increase affinity by 20 points total#   -   z
heart.fillc                     s   ddl m}  z^tD ]>}| d|d |d |d |d |d |d	 |d
 fI dH  qtdtt d W n8 ty } z tjd| dd W Y d}~n
d}~0 0 dS )z
    Insert quest template definitions into database.
    Called during server startup.
    Uses async database pool for proper connection management.
    r   execute_querya  INSERT INTO daily_quest_templates
                   (quest_type, description, progress_required, diamond_reward, difficulty, energy_cost, icon_name)
                   VALUES (%s, %s, %s, %s, %s, %s, %s) AS new_quest
                   ON DUPLICATE KEY UPDATE
                   description=new_quest.description,
                   progress_required=new_quest.progress_required,
                   diamond_reward=new_quest.diamond_rewardr   r   r   r   r   r   r   NzInitialized z daily quest templatesz$Error initializing quest templates: Texc_info)database_asyncr5   QUEST_TEMPLATESlogginginfolen	Exceptionerror)r5   queste rA   7/var/www/lichun.app/lichun/ws/retention/daily_quests.pyinitialize_quest_templates/   s    rC   )	player_idreturnc                    sR  ddl m}m}m} t }z|d| |fI dH }|rf|d dkrftd|  d t| I dH W S |dI dH }|st	d g W S d	d
 |D }dd
 |D }dd
 |D }	|r|r|	st
dt| dt| dt|	  g W S t|t|t|	g}
g }| 4 I dH }| 4 I dH x}|
D ]Z}|d| |d |fI dH  ||j|d |d d|d |d |d |d dd	 q2W d  I dH  q1 I dH s0    Y  W d  I dH  q1 I dH s0    Y  tdt|
 d|   |W S  tyL } z*tj
d|  d| dd g W  Y d}~S d}~0 0 dS ) z
    Assign 3 random quests to player each day (1 easy, 1 medium, 1 hard).
    Called at midnight or on first login of the day.

    Args:
        player_id: The player's ID

    Returns:
        List of assigned quest dictionaries
    r   )	fetch_onefetch_dict_allget_connectionz]SELECT COUNT(*) as count FROM player_daily_quests WHERE player_id = %s AND assigned_date = %sNPlayer z already has quests for todayz#SELECT * FROM daily_quest_templatesz$No quest templates found in databasec                 S   s   g | ]}|d  dkr|qS )r   r   rA   .0qrA   rA   rB   
<listcomp>l       z)generate_daily_quests.<locals>.<listcomp>c                 S   s   g | ]}|d  dkr|qS )r   r    rA   rJ   rA   rA   rB   rM   m   rN   c                 S   s   g | ]}|d  dkr|qS )r   r-   rA   rJ   rA   rA   rB   rM   n   rN   z Missing quest templates - easy: z
, medium: z, hard: zINSERT INTO player_daily_quests
                           (player_id, quest_template_id, assigned_date, progress, completed)
                           VALUES (%s, %s, %s, 0, FALSE)id
quest_typedescriptionprogress_requireddiamond_rewardr   	icon_nameF	rO   rP   rQ   progressrR   rS   r   rT   	completedz	Assigned z daily quests to player z)Error generating daily quests for player : Tr6   )r8   rF   rG   rH   r   todayr:   r;   get_active_questswarningr>   r<   randomchoicecursorexecuteappend	lastrowidr=   )rD   rF   rG   rH   rY   resultZ
all_questsZeasy_questsZmedium_questsZhard_questsselectedZassigned_questsconnr^   r?   r@   rA   rA   rB   generate_daily_questsK   s`    

(
hre   )rD   rP   amountrE   c           	         st  ddl m}m} t }z|d| ||fI dH }|s<W dS t|d | |d }|d||d fI dH  td	|  d
| d| d|d   ||d krt| |d |d I dH  |d |d |d ||d |d |d |d ddd
W S |d |d |d ||d |d |d |d dd	W S W nH t	yn } z.tj
d|  d| d| dd W Y d}~dS d}~0 0 dS )ad  
    Update progress on quest of given type.
    Called after relevant player actions.

    Args:
        player_id: The player's ID
        quest_type: Type of quest to update (e.g., 'talk_to_characters')
        amount: Amount to increment progress (default 1)

    Returns:
        Updated quest dict if quest exists and was updated, None otherwise
    r   )fetch_dict_oner5   ah  SELECT pq.*, qt.quest_type, qt.description, qt.progress_required, qt.diamond_reward, qt.difficulty, qt.icon_name
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.assigned_date = %s
               AND pq.completed = FALSE AND qt.quest_type = %sNrV   rR   z:UPDATE player_daily_quests SET progress = %s WHERE id = %srO   rI   z quest 'z' progress: /rS   rP   rQ   r   rT   T)
rO   rP   rQ   rV   rR   rS   r   rT   rW   Zjust_completedFrU   z)Error updating quest progress for player z	, quest 'z': r6   )r8   rg   r5   r   rY   minr:   r;   complete_questr=   r>   )	rD   rP   rf   rg   r5   rY   r?   Znew_progressr@   rA   rA   rB   update_quest_progress   sR    
	

& rk   )rD   quest_idr   rE   c              
      s   ddl m} z\|d|| fI dH  ddlm} || d| |I dH  td|  d| d	| d
 W dS  ty } z.tjd| d|  d| dd W Y d}~dS d}~0 0 dS )z
    Complete quest and award diamonds.

    Args:
        player_id: The player's ID
        quest_id: The quest record ID
        reward: Diamond reward amount

    Returns:
        True if successful, False otherwise
    r   r4   zUPDATE player_daily_quests
               SET completed = TRUE, completed_date = NOW()
               WHERE id = %s AND player_id = %sNaward_diamondsZdaily_quest_rI   z completed quest z
, awarded 	 diamondsTzError completing quest z for player rX   r6   F)r8   r5   monetization.diamond_economyrn   r:   r;   r=   r>   )rD   rl   r   r5   rn   r@   rA   rA   rB   rj      s    
 rj   c                    s   ddl m} t }z|d| |fI dH }g }|D ]`}||d |d |d |d |d	 |d
 |d |d t|d |d r|d  ndd
 q2|W S  ty } z*tj	d|  d| dd g W  Y d}~S d}~0 0 dS )z
    Get player's current daily quests.

    Args:
        player_id: The player's ID

    Returns:
        List of quest dictionaries
    r   )rG   a  SELECT pq.id, pq.progress, pq.completed, pq.completed_date,
                      qt.quest_type, qt.description, qt.progress_required,
                      qt.diamond_reward, qt.difficulty, qt.icon_name
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.assigned_date = %s
               ORDER BY qt.difficulty DESCNrO   rP   rQ   rV   rR   rS   r   rT   rW   completed_date)
rO   rP   rQ   rV   rR   rS   r   rT   rW   rq   z'Error getting active quests for player rX   Tr6   )
r8   rG   r   rY   r`   bool	isoformatr=   r:   r>   )rD   rG   rY   questsrb   r?   r@   rA   rA   rB   rZ   
  s2    



rZ   c              
   C   s^  d}d}z0zt  }|jdd}|d| f | }|d| f | }|rX|d nd|rr|d rrt|d nd|r|d	 ndd
d |D dW W |r|  |r|  S  ty } zRtj	d|  d| dd dddi dW  Y d}~W |r|  |r|  S d}~0 0 W |r,|  |rZ|  n|rJ|  |rX|  0 dS )z
    Get player's quest completion statistics.

    Args:
        player_id: The player's ID

    Returns:
        Dict with quest statistics
    NT
dictionaryaw  SELECT
                   COUNT(*) as total_completed,
                   SUM(qt.diamond_reward) as total_diamonds_earned,
                   COUNT(DISTINCT pq.assigned_date) as days_completed
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.completed = TRUEa$  SELECT qt.difficulty,
                      COUNT(*) as completed_count
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.completed = TRUE
               GROUP BY qt.difficultytotal_completedr   total_diamonds_earnedZdays_completedc                 S   s   i | ]}|d  |d qS )r   Zcompleted_countrA   )rK   itemrA   rA   rB   
<dictcomp>h  rN   z(get_quest_statistics.<locals>.<dictcomp>)rw   rx   Zdays_with_questsby_difficultyz*Error getting quest statistics for player rX   r6   )
get_database_connectionr^   r_   fetchonefetchallintcloser=   r:   r>   )rD   rd   r^   statsr{   r@   rA   rA   rB   get_quest_statistics:  sZ    

	
r   )r?   rE   c                 C   s   ddl m } dddd}dddddddddddd	}| d
d}|||| ddd}| ddot| ddu}t| d d| d v r| d dd n| d dd | d | | d ddd| d | d | d |d	S )z
    Format quest data to match iOS DailyQuest model expectations.

    Args:
        quest: Raw quest dictionary from database

    Returns:
        Formatted quest dictionary matching iOS model
    r   )r   
activitiescareerZwealth)r   r    r-   social	education)r   r   r#   r1   r   r.   r   r   r(   r&   r+   rP    r   r   rW   Frq   NrO   z - rQ   r/   rS   diamondsr   moneyrV   rR   )	rO   namerQ   categoryr   rV   targetrW   claimed)r   getstrsplit
capitalize)r?   r   Zcategory_mapZtype_to_categoryrP   r   r   rA   rA   rB   format_quest_for_client{  sB    
,r   )rD   rl   rE   c              
   C   s(  d}d}zzXt  }|jdd}|dt|| f | }|sjddddW W |r\|  |rh|  S |d sdd	ddW W |r|  |r|  S |d
 rddddW W |r|  |r|  S |dt|| f ddlm} || |d d |  t	
d|  d| d|d  d dd|d  d|d ddddW W |rX|  |rf|  S  ty } zd|r|  t	jd|  d| d| dd ddddW  Y d}~W |r|  |r|  S d}~0 0 W |r|  |r$|  n|r|  |r"|  0 dS )z
    Claim reward for a completed quest.

    Args:
        player_id: The player's ID
        quest_id: The quest ID to claim

    Returns:
        Dictionary with success status and reward info
    NTru   zSELECT pq.*, qt.diamond_reward
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.id = %s AND pq.player_id = %sFzQuest not found)successr   r   rW   zQuest not completed yetrq   zReward already claimedztUPDATE player_daily_quests
               SET completed_date = NOW()
               WHERE id = %s AND player_id = %sr   rm   rS   zDaily Quest RewardrI   z claimed quest z	 reward: ro   zClaimed z
 diamonds!r   z'Error claiming quest reward for player z, quest rX   r6   zFailed to claim reward)r|   r^   r_   r   r}   r   rp   rn   commitr:   r;   r=   rollbackr>   )rD   rl   rd   r^   r?   rn   r@   rA   rA   rB   claim_quest_reward  s    
:3+
"
 
r   )rD   c              
   C   s   zddl m}m }m} t| }|s*t| }|r<dd |D ng }| }||dd }|||j 	 }	|||j 	 }
|| d||	|
d t
d	|  d
t| d W n> ty } z&t
jd|  d
| dd W Y d}~n
d}~0 0 dS )z
    WebSocket handler for daily quest check.
    Called when player connects to server or at midnight.

    Args:
        player_id: The player's ID
        send_to_client: Function to send messages to client (player_id, message_dict)
    r   )r   r   	timedeltac                 S   s   g | ]}t |qS rA   )r   rJ   rA   rA   rB   rM   &  rN   z,handle_daily_quest_check.<locals>.<listcomp>r   )daysZdailyQuestsStatus)r   rt   ZlastResetDatenextResetDatez"Sent daily quests state to player rX   z questsz,Error handling daily quest check for player Tr6   N)r   r   r   rZ   re   rY   combineri   timers   r:   r;   r<   r=   r>   )rD   send_to_clientr   r   r   Zactive_questsZformatted_questsrY   tomorrowZ
last_reset
next_resetr@   rA   rA   rB   handle_daily_quest_check  s&    	 r   c                   C   s   dS )aI  
    Example: Update quest when player talks to a character

    Integration point: ws/conversationEvents.py or wherever conversations are handled

    Add this after a successful conversation:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'talk_to_characters', 1)
    NrA   rA   rA   rA   rB    example_integration_conversationC  s    
r   c                   C   s   dS )a  
    Example: Update quest when player works

    Integration point: ws/intradayActivity.py or work event handlers

    Add this after work hours are completed:
        from retention.daily_quests import update_quest_progress

        # Track work hours
        update_quest_progress(player_id, 'work_hours', hours_worked)

        # Track activity completion
        update_quest_progress(player_id, 'complete_activities', 1)
    NrA   rA   rA   rA   rB   example_integration_workP  s    r   c                   C   s   dS )a_  
    Example: Update quest when player studies or attends class

    Integration point: Education/school event handlers

    Add this after attending class:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'attend_class', 1)
        update_quest_progress(player_id, 'study', hours_studied)
    NrA   rA   rA   rA   rB   example_integration_studyb  s    r   c                   C   s   dS )a  
    Example: Update quest when player makes a purchase

    Integration point: Store/purchase handlers

    Add this after successful purchase:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'buy_item', 1)
    NrA   rA   rA   rA   rB   example_integration_shoppingp  s    
r   c                   C   s   dS )a  
    Example: Update quest when player goes on a date

    Integration point: Dating/relationship event handlers

    Add this when date activity starts:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'go_on_date', 1)
    NrA   rA   rA   rA   rB   example_integration_dating}  s    
r   c                   C   s   dS )a!  
    Example: Update quest when player spends energy

    Integration point: Activity/energy deduction handlers

    Add this when energy is spent:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'spend_energy', energy_amount)
    NrA   rA   rA   rA   rB   example_integration_energy  s    
r   c                   C   s   dS )a  
    Example: Update quest when player earns money

    Integration point: Work/income event handlers

    Add this when money is earned:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'earn_money', amount_earned)
    NrA   rA   rA   rA   rB   example_integration_money  s    
r   c                   C   s   dS )a.  
    Example: Update quest when affinity increases

    Integration point: Relationship/affinity update handlers

    Add this when affinity increases:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'increase_affinity', affinity_increase)
    NrA   rA   rA   rA   rB   example_integration_affinity  s    
r   c                   C   s   dS )a  
    Example: Update quest when player socializes

    Integration point: Social activity handlers

    Add this when social activity completes:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'socialize', 1)
    NrA   rA   rA   rA   rB   example_integration_socialize  s    
r   )r   )!__doc__typingr   r   r   r   r   r   r:   r\   asyncior9   rC   r   r   re   rk   rr   rj   rZ   r   r   r   r   r   r   r   r   r   r   r   r   r   rA   rA   rA   rB   <module>   sr   Q"J$0A :]1