from fastapi import Depends, HTTPException, status, APIRouter from fastapi.security import OAuth2PasswordBearer from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update import logging from database import get_db from models import Account_User, Customer_Customer, Customer_Description, Card from schemas import TokenData, CustomerUpdate from jose import JWTError, jwt from config import load_config logger = logging.getLogger(__name__) # Load JWT configuration from environment ApplicationConfig = load_config() SECRET_KEY = ApplicationConfig.JWT_SECRET_KEY ALGORITHM = ApplicationConfig.JWT_ALGORITHM async def get_current_user(token: str = Depends(OAuth2PasswordBearer(tokenUrl="auth/login")), db: AsyncSession = Depends(get_db)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) except JWTError: raise credentials_exception user = await db.execute(select(Account_User).where(Account_User.username == token_data.username)) user = user.scalar_one_or_none() if user is None: raise credentials_exception return user oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login") router = APIRouter() @router.get("/me") async def read_users_me(current_user: Account_User = Depends(get_current_user), db: AsyncSession = Depends(get_db)): # Get customer details for the current user using user_id customer = None if current_user.user_id: customer_result = await db.execute(select(Customer_Customer).where(Customer_Customer.id == current_user.user_id)) customer = customer_result.scalar_one_or_none() # Get house description if exists house_description = None if customer: description_record = await db.execute( select(Customer_Description).where(Customer_Description.customer_id == customer.id) ) description_record = description_record.scalar_one_or_none() if description_record: house_description = description_record.description # Map state code to name state_mapping = {0: "MA", 1: "NH"} # Add more as needed state_name = state_mapping.get(customer.customer_state, str(customer.customer_state)) if customer else None return { "id": current_user.id, "username": current_user.username, "email": current_user.email, "account_number": current_user.account_number, "customer_first_name": customer.customer_first_name if customer else None, "customer_last_name": customer.customer_last_name if customer else None, "customer_address": customer.customer_address if customer else None, "customer_apt": customer.customer_apt if customer else None, "customer_town": customer.customer_town if customer else None, "customer_state": state_name, "customer_zip": customer.customer_zip if customer else None, "customer_phone_number": customer.customer_phone_number if customer else None, "customer_home_type": customer.customer_home_type if customer else None, "customer_email": customer.customer_email if customer else None, "house_description": house_description, "member_since": current_user.member_since, "last_seen": current_user.last_seen } @router.put("/update-customer") async def update_customer_info( customer_data: CustomerUpdate, current_user: Account_User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): if not current_user.user_id: raise HTTPException(status_code=404, detail="Customer not found") try: # Update Customer_Customer table customer_update_data = customer_data.dict(exclude_unset=True) customer_fields = { 'customer_first_name', 'customer_last_name', 'customer_address', 'customer_apt', 'customer_town', 'customer_state', 'customer_zip', 'customer_phone_number', 'customer_home_type', 'customer_email' } customer_update_dict = {k: v for k, v in customer_update_data.items() if k in customer_fields} if customer_update_dict: await db.execute( update(Customer_Customer) .where(Customer_Customer.id == current_user.user_id) .values(**customer_update_dict) ) # Update house description if provided if 'house_description' in customer_update_data and customer_update_data['house_description']: # Get customer record customer = await db.execute( select(Customer_Customer).where(Customer_Customer.id == current_user.user_id) ) customer = customer.scalar_one_or_none() if customer: # Check if description record exists description_record = await db.execute( select(Customer_Description).where(Customer_Description.customer_id == customer.id) ) description_record = description_record.scalar_one_or_none() if description_record: # Update existing await db.execute( update(Customer_Description) .where(Customer_Description.customer_id == customer.id) .values(description=customer_update_data['house_description']) ) else: # Create new new_description = Customer_Description( customer_id=customer.id, account_number=current_user.account_number, company_id=customer.company_id or 1, fill_location=0, description=customer_update_data['house_description'] ) db.add(new_description) await db.commit() # Sync billing info to Authorize.net if address-related fields were updated billing_fields = { 'customer_first_name', 'customer_last_name', 'customer_address', 'customer_town', 'customer_state', 'customer_zip', 'customer_phone_number', 'customer_email' } if any(k in customer_update_dict for k in billing_fields): # Late import to avoid circular dependency from routes.payment.routes import update_customer_profile, update_payment_profile_billing # Refetch the updated customer customer_result = await db.execute( select(Customer_Customer).where(Customer_Customer.id == current_user.user_id) ) updated_customer = customer_result.scalar_one_or_none() if updated_customer and updated_customer.auth_net_profile_id: # Update customer profile in Authorize.net update_customer_profile(updated_customer.auth_net_profile_id, updated_customer) # Update all saved payment profiles with new billing info cards_result = await db.execute( select(Card).where(Card.user_id == updated_customer.id) ) cards = cards_result.scalars().all() for card in cards: if card.auth_net_payment_profile_id: update_payment_profile_billing( customer_profile_id=updated_customer.auth_net_profile_id, payment_profile_id=card.auth_net_payment_profile_id, customer=updated_customer, card=card ) logger.info(f"Synced billing info to Authorize.net for customer {updated_customer.id}") return {"message": "Customer information updated successfully"} except Exception as e: await db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to update customer information: {str(e)}" )