
    5)hY\                     T   U d dl mZmZmZmZm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 d dlmZ d dlmZmZmZ d dlmZ d d	lZd d
lmZmZmZmZ d d	lZd dlm Z   e        ddddddddZ!d Z"de#de#fdZ$de#de#fdZ% G d de      Z& G d de      Z' G d de      Z( G d  d!e      Z) G d" d#e      Z* G d$ d%e      Z+ G d& d'e      Z, G d( d)e&      Z- G d* d+e&      Z. G d, d-e&      Z/ ed.d/0      Z0 e       Z1 ejd                  d1      Z3 ejd                  d2      Z4d	a5ee6d3<   d	a7ee6d4<   e0jq                  d5      d6        Z9e0jq                  d7      d8        Z:d9e#fd:Z;d;e#fd<Z<d=e#d>e=d?efd@Z>d=e#d?edee   fdAZ?e1j                  dBe-ej                  dCgD      dEe-fdF       ZBe1j                  dBee-   dCgG      dH        ZDe1j                  dIee'   dJgG      dKe#fdL       ZEe1j                  dMee(   dJgG       edNdOP       edNdQP      fdRe#dKe#dSe#fdT       ZFe1j                  dUe.ej                  dVgD      dWe.fdX       ZGe1j                  dUee.   dVgG      dY        ZHe1j                  dZe/ej                  d[gD      d\e/fd]       ZIe1j                  dZee/   d[gG      d^        ZJe1j                  d_ee/   d[gG      d`e#fda       ZKe1j                  dbe)d[gG       edNdcP      fdde#dee#fdf       ZMe0j                  e1       eOdgk(  r ej                  e0dhdij       y	y	)k    )FastAPIHTTPExceptionstatus	APIRouterQuery)ListAnyOptionalDict)MongoClient)ObjectId)	InvalidId)load_dotenv)	BaseModelField
ConfigDict)ServerSelectionTimeoutErrorN)datetimedate	timedeltatime)EnumLunesMartesu
   MiércolesJuevesViernesu   SábadoDomingo)r                     c                  H    t        j                         j                  d      S )N%Y-%m-%d)r   nowstrftime     /opt/tws/lhia-sport/api/main.py#get_current_date_in_format_day_onlyr+      s    <<>"":..r)   date_strreturnc                     	 t        j                  | d      }t        |j                            S # t        $ r t        dd      w xY w)uK   Convierte una fecha YYYY-MM-DD a nombre del día en español (ej: 'Lunes').r%     u+   Formato de fecha inválido. Use YYYY-MM-DD.status_codedetail)r   strptimeDAY_MAPweekday
ValueErrorr   )r,   dts     r*   get_day_name_in_spanishr8      sJ    cx4rzz|$$ c4abbcs	   ,/ Astart_time_strc                     	 t        j                  | d      }|t        d      z   }|j                  d      S # t        $ r t        dd      w xY w)zDCalcula la hora de fin sumando una hora a la hora de inicio (HH:MM).z%H:%Mr   )hoursr/   u%   Formato de hora inválido. Use HH:MM.r0   )r   r3   r   r'   r6   r   )r9   
start_timeend_times      r*   get_end_timer>   '   sY    ]&&~w?
	 22  )) ]4[\\]s	   58 Ac                   N    e Zd ZU  edd      Zee   ed<    eddddii      Z	y)	MongoBaseModel_idN)aliasdefaultidTexample60d5ec49b14f6b3d4f4d4e3a)populate_by_namejson_schema_extra)
__name__
__module____qualname__r   rD   r
   str__annotations__r   model_configr(   r)   r*   r@   r@   1   s2    E48B8$t-G&HILr)   r@   c                       e Zd ZU dZ edd      Zeed<    edd      Zeed<    edd      Z	e
e   ed	<    edd
      Zeed<   y)DisponibilidadClubzIModelo de respuesta para MCP: Dispo. Diaria (encontrarBloquesDisponibles).ID del club.descriptionid_clubzNombre del club.nombre-Lista de horas de inicio disponibles (HH:MM).horasDisponiblesu&   Precio más bajo de cancha disponible.precio_minimoN)rI   rJ   rK   __doc__r   rT   rL   rM   rU   rW   r   rX   floatr(   r)   r*   rP   rP   =   sQ    S.9GS9);<FC<"'9h"id3ii 2Z[M5[r)   rP   c                       e Zd ZU dZ edd      Zeed<    edd      Zeed<    edd      Z	eed	<    edd
d      Z
eed<   y)CanchaDisponibleuP   Modelo de respuesta para MCP: Verificación de Disponibilidad (Lista de Canchas).rQ   rR   rT   *ID local de la cancha (e.g., C1, Padel_A).cancha_id_localNombre amigable de la cancha.nombre_canchar   u/   Precio por una hora en esta cancha específica.gtrS   precio_horaN)rI   rJ   rK   rY   r   rT   rL   rM   r^   r`   rc   rZ   r(   r)   r*   r\   r\   D   sN    Z.9GS9 2^_OS_s0OPM3Psq6ghKhr)   r\   c                   N    e Zd ZU dZ edd      Zeed<    edd      Zeed<   y)	RespuestaCancelacionu.   Modelo de respuesta para DELETE (Cancelación).u7   Estado de la reserva tras la acción (ej: 'Cancelada').rR   estadoz$Mensaje informativo para el usuario.mensajeN)	rI   rJ   rK   rY   r   rf   rL   rM   rg   r(   r)   r*   re   re   K   s)    8)bcFCc*PQGSQr)   re   c                   6    e Zd ZU dZ edd      Zee   ed<   y)DisponibilidadHorasu4   Contiene la lista de horas disponibles para un día..rV   rR   horasN)	rI   rJ   rK   rY   r   rj   r   rL   rM   r(   r)   r*   ri   ri   U   s    >S.]^E49^r)   ri   c                   :    e Zd ZU dZ edd      Zeed<   eed<   y)DisponibilidadDiau3   Agrupa las horas disponibles por día de la semana..u,   Día al que aplica (ej: 'Lunes', 'Sábado').rR   
dia_semanahorariosN)	rI   rJ   rK   rY   r   rm   rL   rM   ri   r(   r)   r*   rl   rl   Z   s    =C-[\J\!!r)   rl   c                       e Zd ZU dZ edd      Zeed<    edd      Zeed<    edd	      Z	eed
<    eddd      Z
eed<   y)CanchaDetallez0Detalles de una cancha, anidado dentro del Club..r]   rR   r^   r_   r`   Padelu   Deporte que se practica aquí.tipo_deporter   z$Precio base por una hora de reserva.ra   precio_hora_baseN)rI   rJ   rK   rY   r   r^   rL   rM   r`   rr   rs   rZ   r(   r)   r*   rp   rp   `   sP    : 2^_OS_s0OPM3Pg3STL#T#CA;abebr)   rp   c                       e Zd ZU dZ edd      Zeed<    edd      Zeed<    ee	d	      Z
ee   ed
<    ee	d	      Zee   ed<   y)Clubz9Modelo principal de Club con Canchas y Horarios anidados..u   Nombre del club o instalación.rR   rU   u   Dirección física del club.	direccionu)   Horarios operativos disponibles por día.)default_factoryrS   disponibilidad_semanalz*Lista de canchas disponibles en este club.canchasN)rI   rJ   rK   rY   r   rU   rL   rM   rv   listrx   r   rl   ry   rp   r(   r)   r*   ru   ru   h   sc    C)JKFCK3,JKIsK6;D  _J  7KD!23  K#(Kw#xGT- xr)   ru   c                   J    e Zd ZU  edd      Zeed<    edd      Zeed<   y)Cliente.zNombre completo del cliente.rR   rU   u-   Teléfono de contacto (para identificación).telefonoN)rI   rJ   rK   r   rU   rL   rM   r}   r(   r)   r*   r|   r|   q   s&    )GHFCH#+Z[Hc[r)   r|   c                       e Zd ZU dZ edd      Zeed<    edd      Zeed<    edd      Z	eed	<    edd
      Z
eed<    edd      Zeed<    edd      Zeed<    edd      Zeed<   y)Reservaz;La reserva ahora referencia al Club y a la Cancha LOCAL ID..zID del Club (FK a clubs).rR   rT   z&ID local de la Cancha dentro del Club.r^   z&ID del Cliente que realiza la reserva.
id_clientez!Fecha de la reserva (YYYY-MM-DD).fechazHora de inicio (HH:MM).hora_iniciozHora de fin (HH:MM).hora_fin
Confirmadaz-Estado de la reserva (Confirmada, Cancelada).rf   N)rI   rJ   rK   rY   r   rT   rL   rM   r^   r   r   r   r   rf   r(   r)   r*   r   r   u   s    E*EFGSF 2Z[OS[C-UVJV s(KLE3LS.GHKH#+ABHcB 2abFCbr)   r   zAPI LIA Sports - MongoDBz/api-sports)title	root_path	MONGO_URIMONGO_DB_NAMEclientdbstartupc                  @   t         rt        st        d       t        d      	 t	        t         d      at
        j                  j                  d       t
        t           at        dt                y # t        t        f$ r} t        d|         d aY d } ~ y d } ~ ww xY w)NuC   ERROR: MONGO_URI o MONGO_DB_NAME no están definidos en el entorno.z0MONGO_URI y MONGO_DB_NAME deben estar definidos.i'  )serverSelectionTimeoutMSpingz[DB] Conectado a MongoDB: z&[DB] ERROR FATAL AL CONECTAR A MONGO: )r   r   printr6   r   r   admincommandr   r   	Exception)es    r*   startup_db_clientr      s     MSTKLLYGV$M"*=/:;'3 6qc:;s   AA4 4BBBshutdownc                  P    t         r t         j                          t        d       y y )Nu!   [DB] Conexión a MongoDB cerrada.)r   closer   r(   r)   r*   shutdown_db_clientr      s     12 r)   rD   c                 N    	 t        |       S # t        $ r t        dd      w xY w)Nr/   u   ID inválidor0   )r   r   r   )rD   s    r*   validate_object_idr      s0    D| DNCCDs   
 $namec                 V    t         t        t        j                  d      t         |    S )u;   Helper para verificar la conexión y obtener la colección.u)   Error: La conexión a DB no está activa.r0   )r   r   r   HTTP_503_SERVICE_UNAVAILABLE)r   s    r*   get_collectionr      s,     
z(K(KT  A  	Ad8Or)   collection_namedatamodelc                 r   t        |       }d|v r|j                  d       	 |j                  |      }|j                  d|j                  i      }|r"|j                  d      rt        |d         |d<   |j                  |d      S # t        $ r&}t	        t
        j                  d|  d|       d}~ww xY w)	uC   Helper genérico para insertar datos y devolver el objeto validado.rD   zError DB al crear : r0   NrA   Tfrom_attributes)r   pop
insert_oner   r   r   HTTP_500_INTERNAL_SERVER_ERRORfind_oneinserted_idgetrL   model_validate)r   r   r   
collectionresultr   createds          r*   insert_and_returnr      s    0Jt|TXXd^C&&t, !!5&*<*<"=>G7;;u%GEN8Kwu~>>  C(M(MXjkzj{{}~  ~A  WB  C  	CCs   B 	B6!B11B6c                 <   t        |       }	 |j                         }g }|D ]F  }|j                  d      rt        |d         |d<   |j	                  |j                  |d             H |S # t        $ r&}t        t        j                  d|  d|       d}~ww xY w)u6   Helper genérico para buscar todos y validar la lista.rA   Tr   zError DB al listar r   r0   N)
r   findr   rL   appendr   r   r   r   r   )r   r   r   resultsvalidated_resultsdocr   s          r*   find_all_and_validater      s    0JD//# 	VCwwu~CE
Os5z$$U%9%9#t%9%TU	V !  D(M(MXkl{k||~  @A  B  WC  D  	DDs   AA, ,	B5!BBz/clubs/Clubes)response_modelr1   tags	club_datac                 F    t        d| j                  dd      t              S )zBCrea un nuevo club deportivo con canchas y disponibilidad anidada.clubsTby_aliasexclude_none)r   
model_dumpru   )r   s    r*   create_clubr      s%     Wi&:&:DW[&:&\^bccr)   )r   r   c                  "    t        dt              S )z!Lee todos los clubes registrados.r   )r   ru   r(   r)   r*   
read_clubsr      s     !$//r)   z/clubs/availability/{fecha}Disponibilidadr   c                    t        d       t        d|         t        |       t        d        t        d      }| dd}t        d|        |j                  |      }i }d}|D ]1  }|d	z  }|d
   |d   f}||vrg ||<   ||   j	                  |d          3 t        d| d|        t        d      }|j                         }	g }
|	D ]  }|j                  d      rt        |d         |d<   t        j                  |d      }t        |j                        }t        d|j                   d| d       t        fd|j                  D        g       }|st        d|j                   d d       t        d d|        t               }t        d      }|D ]  }d}|j                  D ]x  }||j                   f}|j                  |g       }||vr6d}t#        ||j$                        }t        d| d|j                    d         nt        d| d|j                    d!       z |s|j'                  |        |sz|
j	                  t)        ||j                  t+        t-        |            |t        d      k7  r|nd"#              t        d$|  d%       |
D ]=  }t        d&|j                   dt/        |j0                         d'|j2                          ? t        d       |
S )(u   
    Busca los bloques de tiempo disponibles para una fecha específica (YYYY-MM-DD)
    en todos los clubes, filtrando las horas que ya están reservadas.
    z<============================================================z<[LOG DISPONIBILIDAD] Buscando disponibilidad para la fecha: u2   [LOG DISPONIBILIDAD] Día de la semana calculado: reservasr   )r   rf   z)[LOG DISPONIBILIDAD] Filtro de reservas: r   r   rT   r^   r   z4[LOG DISPONIBILIDAD] Total de reservas encontradas: z. Mapeo de reservas: r   rA   Tr   z
[LOG CLUB] Procesando Club: z ()c              3   j   K   | ]*  }|j                   k(  s|j                  j                   , y w)N)rm   rn   rj   ).0drm   s     r*   	<genexpr>z0encontrar_bloques_disponibles.<locals>.<genexpr>  s*     pUVUaUaeoUoAJJ,,ps   33z[LOG CLUB] Club z# NO tiene horario configurado para .z&[LOG CLUB] Horario base del club para r   infFz  [LOG HORA] z	: Cancha u    ESTÁ LIBRE.u    ESTÁ OCUPADA.g        )rT   rU   rW   rX   z1
[LOG DISPONIBILIDAD FINAL] Resultado total para :z  - u    horas. Mínimo: )r   r8   r   r   r   r   rL   ru   r   rD   rU   nextrx   setrZ   ry   r^   minrs   addrP   sortedrz   lenrW   rX   )r   reservas_collectionfiltro_reservasreservas_existentesreservas_mapreservas_countreskeyclubs_collectiontodos_los_clubsdisponibilidad_finalclub_docclubclub_idhorario_diahoras_disponibles_club	min_pricer   cancha_libre_en_horacanchahoras_reservadas_canchar   rm   s                         @r*   encontrar_bloques_disponiblesr      sY    
&M	H
PQ )/J	>zl
KL )4 O 
5o5F
GH-22?C ,.LN" 5!9~s#456l" "LS  ]!345 
@@PPefres
tu &g.&++-O57# 2<<!(5/2HUO""8T"Bdgg,.t{{m2gYaHI pd6Q6Qprtu$T[[M1TU_T``abc6zl"[MRS ,/5%L	& 	8K#( ,, i 6 67*6*:*:3*C'&==+/( #Iv/F/F GIM+i@V@V?WWdefM+i@V@V?WWfghi $&**;7#	8& " ''"#;;%+D1G,H%I/8E%L/H)c	W2h 
>ugQ
GH! ^QXXJbQ%7%7!8 99J1??J[\]^ 
&Mr)   z%/clubs/{id_club}/canchas/availability.zFecha de reserva (YYYY-MM-DD)rR   zHora de inicio (HH:MM)rT   r   c           	         t        |       }t        |      }t        d      }|j                  d|i      }|st	        dd|  d      t        |d         |d<   t        j                  |d      }t        d	      }|j                  | ||d
d      }	|	D 
ch c]  }
|
d   	 }}
g }|j                  D ]M  }|j                  |vs|j                  t        | |j                  |j                  |j                               O |S c c}
w )ui   
    Verifica qué canchas específicas están disponibles en un club, fecha y hora de inicio dados.
    r   rA     zClub con ID z no encontrado.r0   Tr   r   r   )rT   r   r   rf   r^   )rT   r^   r`   rc   )r   r>   r   r   r   rL   ru   r   r   ry   r^   r   r\   r`   rs   )rT   r   r   club_id_objr   r   r   r   r   reservas_ocupadasr   canchas_ocupadascanchas_disponiblesr   s                 r*   verificar_disponibilidadr   M  s2    %W-KK(H &g.((%)=>Hl7)?4[\\(5/*HUOx>D )4+00"	2  ;LL3-.LL 35,, 	!!)99&& #$*$:$:"("6"6 & 7 7		 ! Ms   Dz
/clientes/Clientescliente_datac                 F    t        d| j                  dd      t              S )zRegistra un nuevo cliente.clientesTr   )r   r   r|   )r   s    r*   create_clienter     s%     Z)@)@$]a)@)bdkllr)   c                  "    t        dt              S )z#Lee todos los clientes registrados.r   )r   r|   r(   r)   r*   read_clientesr          !W55r)   z
/reservas/Reservasreserva_datac                 z    t        | j                        | _        t        d| j	                  dd      t
              S )zMCrea una nueva reserva (referencia al ID de club y al ID local de la cancha).r   Tr   )r>   r   r   r   r   r   )r   s    r*   create_reservar     s:     ))A)ABL Z)@)@$]a)@)bdkllr)   c                  "    t        dt              S )z#Lee todas las reservas registradas.r   )r   r   r(   r)   r*   read_reservasr     r   r)   z(/reservas/cliente/{identificadorUsuario}identificadorUsuarioc                    t        d      }t        d      }| h}|j                  d| i      }|r|j                  t        |d                ddt	        |      id}|j                  |      }g }|D ]J  }|j                  d      rt        |d         |d<   |j                  t        j                  |d	             L |S )
u   
    Lee las reservas de un cliente usando su identificador (teléfono o ID de Mongo).
    Hemos mejorado la lógica para soportar que el ID de cliente sea un string de teléfono.
    r   r   r}   rA   r   z$in)rf   r   Tr   )
r   r   r   rL   rz   r   r   r   r   r   )	r   r   clientes_collectioncandidatos_id_clientecliente_docr   r   r   r   s	            r*   ver_reservas_clienter    s     )4(4 22 &..
<P/QRK!!#k%&8"9: d#89:O "&&7G T775>CJ3u:  !7!7T!7!RST r)   z/reservas/{id_reserva}uG   Identificador (ID de cliente o teléfono) para validar la cancelación.
id_reservauserIdentifierc                 J   	 t        |       }t        d      }|j	                  d|i      }|st        dd      	 t        |d         |d<   t        j                  |d	      }|j                  |k7  rt        dd      |j                  dk(  rt        dd      |j                  ||ddddii      }|j                  dk(  rt        dd|  d      S t        d
d      # t        $ r t        dd      w xY w# t        $ r}t        d
dt        |             d}~ww xY w)u^   
    Cancela una reserva específica, validando que el userIdentifier sea el propietario.
    r/   u8   El ID de reserva proporcionado no es un formato válido.r0   r   rA   r   zReserva no encontrada.Tr   i  z%Error interno al validar la reserva: Ni  uS   No estás autorizado para cancelar esta reserva. Solo el propietario puede hacerlo.	Canceladaz!Esta reserva ya estaba cancelada.)rA   r   z$setrf   r   zLa reserva z  ha sido cancelada exitosamente.)rf   rg   zBNo se pudo actualizar el estado de la reserva. Contacta a soporte.)r   r   r   r   r   rL   r   r   r   r   rf   
update_onemodified_countre   )r  r  reserva_id_objr   reserva_docreservar   r   s           r*   cancelar_reservar    so   p!*-
 )4 &..~/FGK4LMMf U!34E((d(K ^+  5J  K  	K ~~$4WXX !++n=	(K()F
 !#!*-MN
 	
 C0t
uuW  p4noop"  f6[\_`a\b[c4deefs"   C! (C; !C8;	D"DD"__main__z0.0.0.0iL  )hostport)Qfastapir   r   r   r   r   typingr   r	   r
   r   pymongor   bson.objectidr   bson.errorsr   dotenvr   pydanticr   r   r   pymongo.errorsr   osr   r   r   r   uvicornenumr   r4   r+   rL   r8   r>   r@   rP   r\   re   ri   rl   rp   ru   r|   r   approutergetenvr   r   r   rM   r   on_eventr   r   r   r   dictr   r   postHTTP_201_CREATEDr   r   r   r   r   r   r   r   r   r  deleter  include_routerrI   runr(   r)   r*   <module>r'     s`   D D , ,  " !  1 1 6 	 4 4   
 8Y9
/cc cc c] ] ]Y \ \iy iR9 R_) _
"	 "cI cy> y\n \cn c" .-H	BIIk"			/* 
Ci   j3 3D3 D ?s ?$ ?y ?D3 Dy DT)_ D& Yt9P9PX`Wabd4 d cd Id4jzB0 C0 )$?Q:RZjYklb  b  mb L 3DIYDZbrast s(GHS.FG/// / u/l \'v?V?V^h]ijm m km Lgj\J6 K6 \'v?V?V^h]ijm m km Lgj\J6 K6 6tG}\f[gh s   i D '8LT^S_`  1z{7v7v7v a7v@   6 zGKK r)   