
    `j                        d Z ddlm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 erddlmZ  ej                  e      Zdd	Z	 	 	 	 	 	 dd
Z	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZy)ar  GuestStay synchronization service.

This service ensures that GuestStay rows exist for every (booking, guest) pair,
with proper idswh generation and lifecycle management.

Architecture:
- GuestStay is the canonical regulatory identity root
- idswh is generated once at creation and never recomputed
- Exports (Ross1000, C59, ISTAT) only READ stored identities
- This service is the ONLY place where GuestStay rows are created/updated/deleted

Usage:
    from guests.services.guest_stay_service import sync_guest_stays_for_booking

    # After creating/updating a booking with guests:
    sync_guest_stays_for_booking(booking)
    )annotationsN)date)TYPE_CHECKING)transaction)	GuestStay)Bookingc           	        | j                   }t        | j                  j                  dd            }|sKt        j
                  j                  |       j                         \  }}|rt        j                  d||       yt        t        j
                  j                  |       j                  d            }|D ci c]  }|j                  | }}t        |j                               }||z
  }	||z  }
||z
  }|	rt        | |	       |
rt        | |
|       |rt!        | |       t        j                  d|t#        |	      t#        |
      t#        |             yc c}w )	a  Synchronize GuestStay rows for a booking and its guests.

    This function ensures that:
    1. A GuestStay exists for every guest currently linked to the booking
    2. Existing GuestStay rows are preserved (idswh never changes)
    3. Check-in/check-out dates are synced from the booking
    4. Orphan GuestStay rows (for removed guests) are deleted
    5. The operation is idempotent (safe to run multiple times)

    Business rules:
    - idswh is generated automatically by GuestStay.save() on first creation
    - Once generated, idswh is NEVER modified or regenerated
    - GuestStay lifecycle is tied to booking guest membership, not check-in status
    - Identity exists for the entire stay lifecycle (from booking creation onward)

    Args:
        booking: The Booking instance to sync GuestStay rows for.

    Performance:
    - Single query to fetch existing GuestStay rows
    - Single query to fetch current guests
    - Bulk delete for orphan rows
    - No N+1 queries

    Example:
        >>> booking = Booking.objects.create(...)
        >>> sync_booking_guests(booking=booking, guests_data=[...])
        >>> sync_guest_stays_for_booking(booking)  # Creates GuestStay rows
    idT)flat)bookingz=GuestStay: removed %d orphan stays for booking %d (no guests)NguestuC   GuestStay: synced booking %d — created=%d, updated=%d, deleted=%d)r
   setguestsvalues_listr   objectsfilterdeleteloggerinfolistselect_relatedguest_idkeys_create_guest_stays_update_guest_stay_dates_delete_orphan_guest_stayslen)r   
booking_idcurrent_guest_idsdeleted_count_existing_staysstayexisting_by_guest_idexisting_guest_idsguest_ids_to_createguest_ids_to_updateguest_ids_to_deletes               ./backend/guests/services/guest_stay_service.pysync_guest_stays_for_bookingr*   $   sm   < J ""4d"3 $,,33G3DKKMqKKO
 	    1@@IN =KKNDDMM4/NK16689 ,.@@+.@@,/@@ G%89   	
 "7,?@
KKM   1 Ls   <Ec                8   | j                   }| j                  }| j                  }d}|D ]+  }	 t        j                  j                  | |||       |dz  }- |rt        j                  d||       yy# t        $ r}t        j                  d|||        d}~ww xY w)a  Create GuestStay rows for new guests.

    Each GuestStay is created individually to trigger the save() method,
    which auto-generates the idswh token.

    Args:
        booking: The parent booking.
        guest_ids: Set of guest IDs that need GuestStay rows created.
    r   )r   r   check_in_datecheck_out_date   z>GuestStay: failed to create stay for guest %d / booking %d: %sNz.GuestStay: created %d new stays for booking %d)
r
   r,   r-   r   r   create	Exceptionr   errorr   )r   	guest_idsr   check_in	check_outcreated_countr   excs           r)   r   r   y   s     J$$H&&IM	$$!&(	 %  QM $ <	
   	LLP	 	s   (A22	B;BBc                   | j                   }| j                  }| j                  }g }|D ]T  }|j                  |      }||j                  |k7  s|j                  |k7  s6||_        ||_        |j	                  |       V |rDt
        j                  j                  |g d       t        j                  dt        |      |       yy)uf  Update check-in/check-out dates on existing GuestStay rows.

    This preserves the idswh token while keeping dates in sync with the booking.

    Args:
        booking: The parent booking (source of truth for dates).
        guest_ids: Set of guest IDs that need date updates.
        existing_by_guest_id: Dict mapping guest_id → GuestStay instance.
    N)r,   r-   
updated_at)fieldsz3GuestStay: updated dates for %d stays on booking %d)r
   r,   r-   getappendr   r   bulk_updater   r   r   )	r   r2   r$   r   r3   r4   stays_to_updater   r#   s	            r)   r   r      s     J$$H&&IO#''1< )T-@-@I-M!)D"+D""4(  %%D 	& 	
 	A 	
     c                    | j                   }t        j                  j                  | |      j	                         \  }}|r"t
        j                  d||t        |             yy)zDelete GuestStay rows for guests that were removed from the booking.

    Args:
        booking: The parent booking.
        guest_ids: Set of guest IDs whose GuestStay rows should be deleted.
    )r   guest_id__inz>GuestStay: deleted %d orphan stays for booking %d (guests: %s)N)r
   r   r   r   r   r   r   sorted)r   r2   r   r    r!   s        r)   r   r      sg     J !((// 0  fh M1
 L9		
 r>   )r   	'Booking'returnNone)r   rB   r2   set[int]rC   rD   )r   rB   r2   rE   r$   zdict[int, GuestStay]rC   rD   )__doc__
__future__r   loggingdatetimer   typingr   	django.dbr   guests.modelsr   bookings.modelsr   	getLogger__name__r   r*   r   r   r    r>   r)   <module>rQ      s   $ #     ! #' 
		8	$Rj)
)
)
 
)
X(
(
(
 /(
 
	(
V


 

r>   