from fastapi import HTTPException from app.core.nosql_client import db from app.utils.common_utils import is_email, is_phone, validate_identifier # Updated imports from app.schemas.user_schema import UserRegisterRequest import logging logger = logging.getLogger("user_model") class BookMyServiceUserModel: collection = db["customers"] @staticmethod async def find_by_email(email: str): logger.info(f"Searching for user by email: {email}") try: user = await BookMyServiceUserModel.collection.find_one({"email": email}) if user: logger.info(f"User found by email: {email}") else: logger.info(f"No user found with email: {email}") return user except Exception as e: logger.error(f"Error finding user by email {email}: {str(e)}", exc_info=True) return None @staticmethod async def find_by_phone(phone: str): logger.info(f"Searching for user by phone: {phone}") try: user = await BookMyServiceUserModel.collection.find_one({"phone": phone}) if user: logger.info(f"User found by phone: {phone}") else: logger.info(f"No user found with phone: {phone}") return user except Exception as e: logger.error(f"Error finding user by phone {phone}: {str(e)}", exc_info=True) return None @staticmethod async def find_by_mobile(mobile: str): """Legacy method for backward compatibility - redirects to find_by_phone""" logger.info(f"Legacy find_by_mobile called, redirecting to find_by_phone for: {mobile}") return await BookMyServiceUserModel.find_by_phone(mobile) @staticmethod async def find_by_identifier(identifier: str): logger.info(f"Finding user by identifier: {identifier}") try: # Validate and determine identifier type identifier_type = validate_identifier(identifier) logger.info(f"Identifier type determined: {identifier_type}") if identifier_type == "email": logger.info(f"Searching by email for identifier: {identifier}") user = await BookMyServiceUserModel.find_by_email(identifier) elif identifier_type == "phone": logger.info(f"Searching by phone for identifier: {identifier}") user = await BookMyServiceUserModel.find_by_phone(identifier) else: logger.error(f"Invalid identifier type: {identifier_type}") raise HTTPException(status_code=400, detail="Invalid identifier format") if not user: logger.warning(f"User not found with identifier: {identifier}") raise HTTPException(status_code=404, detail="User not found with this email or phone") logger.info(f"User found successfully for identifier: {identifier}") logger.info(f"User data keys: {list(user.keys()) if user else 'None'}") return user except ValueError as ve: logger.error(f"Validation error for identifier {identifier}: {str(ve)}") raise HTTPException(status_code=400, detail=str(ve)) except HTTPException as e: logger.error(f"HTTP error finding user by identifier {identifier}: {e.status_code} - {e.detail}") raise e except Exception as e: logger.error(f"Unexpected error finding user by identifier {identifier}: {str(e)}", exc_info=True) raise HTTPException(status_code=500, detail="Failed to find user") @staticmethod async def exists_by_email_or_phone(email: str = None, phone: str = None) -> bool: """Check if user exists by email or phone""" query_conditions = [] if email: query_conditions.append({"email": email}) if phone: query_conditions.append({"phone": phone}) if not query_conditions: return False query = {"$or": query_conditions} if len(query_conditions) > 1 else query_conditions[0] result = await BookMyServiceUserModel.collection.find_one(query) return result is not None @staticmethod async def create(user_data: UserRegisterRequest): user_dict = user_data.dict() result = await BookMyServiceUserModel.collection.insert_one(user_dict) return result.inserted_id @staticmethod async def update_by_identifier(identifier: str, update_fields: dict): try: identifier_type = validate_identifier(identifier) if identifier_type == "email": query = {"email": identifier} elif identifier_type == "phone": query = {"phone": identifier} else: raise HTTPException(status_code=400, detail="Invalid identifier format") result = await BookMyServiceUserModel.collection.update_one(query, {"$set": update_fields}) if result.matched_count == 0: raise HTTPException(status_code=404, detail="User not found") return result.modified_count > 0 except ValueError as ve: logger.error(f"Validation error for identifier {identifier}: {str(ve)}") raise HTTPException(status_code=400, detail=str(ve)) @staticmethod async def update_profile(customer_id: str, update_fields: dict): """Update user profile by customer_id""" try: from datetime import datetime # Add updated_at timestamp update_fields["updated_at"] = datetime.utcnow() result = await BookMyServiceUserModel.collection.update_one( {"customer_id": customer_id}, {"$set": update_fields} ) if result.matched_count == 0: raise HTTPException(status_code=404, detail="User not found") return result.modified_count > 0 except Exception as e: logger.error(f"Error updating profile for user {customer_id}: {str(e)}") raise HTTPException(status_code=500, detail="Failed to update profile") @staticmethod async def find_by_id(customer_id: str): """Find user by customer_id""" try: user = await BookMyServiceUserModel.collection.find_one({"customer_id": customer_id}) return user except Exception as e: logger.error(f"Error finding user by ID {customer_id}: {str(e)}") return None