from jose import jwt
import bcrypt
from datetime import datetime, timedelta
import secrets
import hashlib
from sqlalchemy.orm import Session
from typing import Optional
import logging

from ..models.user import User
from ..models.refresh_token import RefreshToken
from ..models.password_reset_token import PasswordResetToken
from ..models.email_verification_token import EmailVerificationToken
from ..models.password_history import PasswordHistory
from ..models.role import Role
from ...shared.utils.mailer import send_email
from ...shared.utils.email_templates import (
    welcome_email_template,
    password_reset_email_template,
    password_changed_email_template,
    email_verification_template,
    email_changed_verification_template
)
from ...auth.services.session_service import session_service
from ...shared.config.settings import settings
import os

logger = logging.getLogger(__name__)

class AuthService:
    def __init__(self):
        # Security: Fail fast if JWT_SECRET is not configured - never use default values
        self.jwt_secret = getattr(settings, 'JWT_SECRET', None) or os.getenv("JWT_SECRET")
        if not self.jwt_secret:
            error_msg = (
                'CRITICAL: JWT_SECRET is not configured. '
                'Please set JWT_SECRET environment variable to a secure random string (minimum 64 characters). '
                'Generate one using: python -c "import secrets; print(secrets.token_urlsafe(64))"'
            )
            logger.error(error_msg)
            # SECURITY: Always fail if JWT_SECRET is not configured, even in development
            # This prevents accidental deployment without proper secrets
            raise ValueError(error_msg)
        
        # Validate JWT secret strength
        if len(self.jwt_secret) < 32:
            error_msg = 'JWT_SECRET must be at least 32 characters long for security.'
            logger.error(error_msg)
            if settings.is_production:
                raise ValueError(error_msg)
            else:
                logger.warning(error_msg)
        
        # SECURITY: Validate JWT secret entropy (check for predictable patterns)
        # Check if secret appears to be randomly generated (not a simple pattern)
        if len(set(self.jwt_secret)) < len(self.jwt_secret) * 0.3:  # Less than 30% unique characters suggests low entropy
            error_msg = 'JWT_SECRET appears to have low entropy. Please use a randomly generated secret.'
            logger.error(error_msg)
            if settings.is_production:
                raise ValueError(error_msg)
            else:
                logger.warning(error_msg)
        
        # Refresh secret should be different from access secret
        self.jwt_refresh_secret = os.getenv("JWT_REFRESH_SECRET")
        if not self.jwt_refresh_secret:
            # Use a derived secret if not explicitly set, but different from access secret
            import hashlib
            self.jwt_refresh_secret = hashlib.sha256((self.jwt_secret + "-refresh").encode()).hexdigest()
            if not settings.is_production:
                logger.info('JWT_REFRESH_SECRET not set, using derived secret')
        
        self.jwt_expires_in = os.getenv("JWT_EXPIRES_IN", "1h")
        self.jwt_refresh_expires_in = os.getenv("JWT_REFRESH_EXPIRES_IN", "7d")

    def generate_tokens(self, user_id: int) -> dict:
        from datetime import datetime, timedelta
        
        # SECURITY: Add standard JWT claims for better security
        now = datetime.utcnow()
        access_expires = now + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
        refresh_expires = now + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
        
        access_payload = {
            "userId": user_id,
            "exp": access_expires,  # Expiration time
            "iat": now,  # Issued at
            "iss": settings.APP_NAME,  # Issuer
            "type": "access"  # Token type
        }
        
        refresh_payload = {
            "userId": user_id,
            "exp": refresh_expires,  # Expiration time
            "iat": now,  # Issued at
            "iss": settings.APP_NAME,  # Issuer
            "type": "refresh"  # Token type
        }
        
        access_token = jwt.encode(
            access_payload,
            self.jwt_secret,
            algorithm="HS256"
        )

        refresh_token = jwt.encode(
            refresh_payload,
            self.jwt_refresh_secret,
            algorithm="HS256"
        )

        return {"accessToken": access_token, "refreshToken": refresh_token}

    def verify_access_token(self, token: str) -> dict:
        return jwt.decode(token, self.jwt_secret, algorithms=["HS256"])

    def verify_refresh_token(self, token: str) -> dict:
        return jwt.decode(token, self.jwt_refresh_secret, algorithms=["HS256"])

    def hash_password(self, password: str) -> str:

        password_bytes = password.encode('utf-8')

        salt = bcrypt.gensalt()
        hashed = bcrypt.hashpw(password_bytes, salt)
        return hashed.decode('utf-8')

    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        try:
            password_bytes = plain_password.encode('utf-8')
            hashed_bytes = hashed_password.encode('utf-8')
            return bcrypt.checkpw(password_bytes, hashed_bytes)
        except Exception:
            return False

    def format_user_response(self, user: User) -> dict:
        return {
            "id": user.id,
            "name": user.full_name,
            "email": user.email,
            "phone": user.phone,
            "avatar": user.avatar,
            "currency": getattr(user, 'currency', 'VND'),
            "role": user.role.name if user.role else "customer",
            "emailVerified": getattr(user, 'email_verified', True),  # Default to True for backward compatibility
            "createdAt": user.created_at.isoformat() if user.created_at else None,
            "updatedAt": user.updated_at.isoformat() if user.updated_at else None,
        }

    async def register(self, db: Session, name: str, email: str, password: str, phone: Optional[str] = None) -> dict:
        # Validate password strength
        from ...shared.utils.password_validation import validate_password_strength
        is_valid, errors = validate_password_strength(password)
        if not is_valid:
            error_message = 'Password does not meet requirements: ' + '; '.join(errors)
            raise ValueError(error_message)

        existing_user = db.query(User).filter(User.email == email).first()
        if existing_user:
            raise ValueError("Email already registered")

        hashed_password = self.hash_password(password)

        user = User(
            full_name=name,
            email=email,
            password=hashed_password,
            phone=phone,
            role_id=3,
            email_verified=False,  # Email not verified on registration
            password_changed_at=datetime.utcnow()  # Set initial password change timestamp
        )
        db.add(user)
        db.commit()
        db.refresh(user)

        user.role = db.query(Role).filter(Role.id == user.role_id).first()

        # Generate email verification token
        verification_token = secrets.token_hex(32)
        hashed_verification_token = hashlib.sha256(verification_token.encode()).hexdigest()
        expires_at = datetime.utcnow() + timedelta(hours=24)  # 24 hour expiration
        
        verification_token_obj = EmailVerificationToken(
            user_id=user.id,
            token=hashed_verification_token,
            email=email,
            expires_at=expires_at
        )
        db.add(verification_token_obj)
        db.commit()

        tokens = self.generate_tokens(user.id)

        expires_at = datetime.utcnow() + timedelta(days=7)
        refresh_token = RefreshToken(
            user_id=user.id,
            token=tokens["refreshToken"],
            expires_at=expires_at
        )
        db.add(refresh_token)
        db.commit()

        # Send welcome email with verification link
        try:
            client_url = settings.CLIENT_URL or os.getenv("CLIENT_URL", "http://localhost:5173")
            verification_url = f"{client_url}/verify-email/{verification_token}"
            email_html = email_verification_template(verification_url, user.full_name)
            await send_email(
                to=user.email,
                subject="Welcome to Hotel Booking - Verify Your Email",
                html=email_html
            )
            logger.info(f"Verification email sent successfully to {user.email}")
        except Exception as e:
            logger.error(f"Failed to send verification email to {user.email}: {type(e).__name__}: {str(e)}", exc_info=True)

        return {
            "user": self.format_user_response(user),
            "token": tokens["accessToken"],
            "refreshToken": tokens["refreshToken"]
        }

    async def login(self, db: Session, email: str, password: str, remember_me: bool = False, mfa_token: str = None, expected_role: str = None) -> dict:

        email = email.lower().strip() if email else ""
        if not email:
            raise ValueError("Invalid email or password")
        

        user = db.query(User).filter(User.email == email).first()
        if not user:
            logger.warning(f"Login attempt with non-existent email: {email}")
            raise ValueError("Invalid email or password")

        if not user.is_active:
            logger.warning(f"Login attempt for inactive user: {email}")
            raise ValueError("Account is disabled. Please contact support.")
        
        # Check password expiry (if enabled)
        if settings.PASSWORD_EXPIRY_DAYS > 0 and user.password_changed_at:
            expiry_date = user.password_changed_at + timedelta(days=settings.PASSWORD_EXPIRY_DAYS)
            if datetime.utcnow() > expiry_date:
                days_expired = (datetime.utcnow() - expiry_date).days
                raise ValueError(f"Your password has expired {days_expired} day(s) ago. Please reset your password to continue.")

        # Check if account is locked (reset if lockout expired)
        if user.locked_until:
            if user.locked_until > datetime.utcnow():
                logger.warning(f"Login attempt for locked account: {email} (locked until {user.locked_until})")
                # SECURITY: Don't reveal specific lockout duration to prevent timing attacks
                raise ValueError("Account is temporarily locked due to multiple failed login attempts. Please try again later.")
            else:
                # Lockout expired, reset it
                user.locked_until = None
                user.failed_login_attempts = 0
                db.commit()

        user.role = db.query(Role).filter(Role.id == user.role_id).first()

        password_valid = self.verify_password(password, user.password)
        
        # Handle failed login attempt
        if not password_valid:
            user.failed_login_attempts = (user.failed_login_attempts or 0) + 1
            max_attempts = settings.MAX_LOGIN_ATTEMPTS
            lockout_duration = settings.ACCOUNT_LOCKOUT_DURATION_MINUTES
            
            # Lock account if max attempts reached
            if user.failed_login_attempts >= max_attempts:
                user.locked_until = datetime.utcnow() + timedelta(minutes=lockout_duration)
                logger.warning(f"Account locked due to {user.failed_login_attempts} failed login attempts: {email}")
                db.commit()
                # SECURITY: Don't reveal specific lockout duration
                raise ValueError("Account has been temporarily locked due to multiple failed login attempts. Please try again later.")
            else:
                logger.warning(f"Login attempt with invalid password for user: {email} ({user.failed_login_attempts}/{max_attempts} failed attempts)")
                db.commit()
                # SECURITY: Don't reveal remaining attempts to prevent enumeration
                raise ValueError("Invalid email or password")

        # Check email verification (warn but allow login for better UX)
        email_verification_required = not user.email_verified
        if email_verification_required:
            logger.info(f"Login attempt for unverified email: {email}")

        if user.mfa_enabled:
            if not mfa_token:

                return {
                    "requires_mfa": True,
                    "user_id": user.id,
                    "email_verified": user.email_verified
                }
            

            from ..services.mfa_service import mfa_service
            is_backup_code = len(mfa_token) == 8
            if not mfa_service.verify_mfa(db, user.id, mfa_token, is_backup_code):
                # Increment failed attempts on MFA failure
                user.failed_login_attempts = (user.failed_login_attempts or 0) + 1
                max_attempts = settings.MAX_LOGIN_ATTEMPTS
                lockout_duration = settings.ACCOUNT_LOCKOUT_DURATION_MINUTES
                
                if user.failed_login_attempts >= max_attempts:
                    user.locked_until = datetime.utcnow() + timedelta(minutes=lockout_duration)
                    logger.warning(f"Account locked due to {user.failed_login_attempts} failed attempts (MFA failure): {email}")
                    db.commit()
                    # SECURITY: Don't reveal specific lockout duration
                    raise ValueError("Account has been temporarily locked due to multiple failed login attempts. Please try again later.")
                else:
                    db.commit()
                    # SECURITY: Don't reveal remaining attempts
                    raise ValueError("Invalid MFA token")

        # Validate role if expected_role is provided (for role-specific login endpoints)
        if expected_role:
            user_role = user.role.name.lower() if user.role and hasattr(user.role, 'name') else None
            expected_role_lower = expected_role.lower()
            if user_role != expected_role_lower:
                logger.warning(f"Role mismatch: user {email} has role {user_role} but expected {expected_role_lower}")
                raise ValueError(f"This login endpoint is only for {expected_role_lower} users")
        
        # Reset failed login attempts and unlock account on successful login
        if user.failed_login_attempts > 0 or user.locked_until:
            user.failed_login_attempts = 0
            user.locked_until = None
            db.commit()

        tokens = self.generate_tokens(user.id)

        expiry_days = 7 if remember_me else 1
        expires_at = datetime.utcnow() + timedelta(days=expiry_days)

        try:
            db.query(RefreshToken).filter(
                RefreshToken.user_id == user.id
            ).delete()
            db.flush()

            refresh_token = RefreshToken(
                user_id=user.id,
                token=tokens["refreshToken"],
                expires_at=expires_at
            )
            db.add(refresh_token)
            db.commit()
        except Exception as e:
            db.rollback()
            logger.error(f"Error saving refresh token for user {user.id}: {str(e)}", exc_info=True)
            try:
                db.query(RefreshToken).filter(
                    RefreshToken.token == tokens["refreshToken"]
                ).delete()
                db.flush()
                refresh_token = RefreshToken(
                    user_id=user.id,
                    token=tokens["refreshToken"],
                    expires_at=expires_at
                )
                db.add(refresh_token)
                db.commit()
            except Exception as retry_error:
                db.rollback()
                logger.error(f"Retry failed for refresh token: {str(retry_error)}", exc_info=True)
                raise ValueError("Failed to create session. Please try again.")

        return {
            "user": self.format_user_response(user),
            "token": tokens["accessToken"],
            "refreshToken": tokens["refreshToken"]
        }

    async def refresh_access_token(self, db: Session, refresh_token_str: str) -> dict:
        if not refresh_token_str:
            raise ValueError("Refresh token is required")

        decoded = self.verify_refresh_token(refresh_token_str)

        stored_token = db.query(RefreshToken).filter(
            RefreshToken.token == refresh_token_str,
            RefreshToken.user_id == decoded["userId"]
        ).first()

        if not stored_token:
            raise ValueError("Invalid refresh token")

        if datetime.utcnow() > stored_token.expires_at:
            db.delete(stored_token)
            db.commit()
            raise ValueError("Refresh token expired")

        from datetime import datetime, timedelta
        
        # SECURITY: Add standard JWT claims when refreshing token
        now = datetime.utcnow()
        access_expires = now + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
        
        access_payload = {
            "userId": decoded["userId"],
            "exp": access_expires,  # Expiration time
            "iat": now,  # Issued at
            "iss": settings.APP_NAME,  # Issuer
            "type": "access"  # Token type
        }
        
        access_token = jwt.encode(
            access_payload,
            self.jwt_secret,
            algorithm="HS256"
        )

        return {"token": access_token}

    async def logout(self, db: Session, refresh_token_str: str) -> bool:
        if refresh_token_str:
            db.query(RefreshToken).filter(RefreshToken.token == refresh_token_str).delete()
            db.commit()
        return True

    async def get_profile(self, db: Session, user_id: int) -> dict:
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        user.role = db.query(Role).filter(Role.id == user.role_id).first()

        return self.format_user_response(user)

    async def update_profile(
        self, 
        db: Session, 
        user_id: int, 
        full_name: Optional[str] = None,
        email: Optional[str] = None,
        phone_number: Optional[str] = None,
        password: Optional[str] = None,
        current_password: Optional[str] = None,
        currency: Optional[str] = None
    ) -> dict:
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        if password:
            if not current_password:
                raise ValueError("Current password is required to change password")
            if not self.verify_password(current_password, user.password):
                raise ValueError("Current password is incorrect")
            
            # Check password minimum age
            if user.password_changed_at and settings.PASSWORD_MIN_AGE_DAYS > 0:
                min_age_date = user.password_changed_at + timedelta(days=settings.PASSWORD_MIN_AGE_DAYS)
                if datetime.utcnow() < min_age_date:
                    days_remaining = (min_age_date - datetime.utcnow()).days + 1
                    raise ValueError(f"Password cannot be changed yet. Minimum age is {settings.PASSWORD_MIN_AGE_DAYS} days. Please try again in {days_remaining} day(s).")
            
            # Validate new password strength
            from ...shared.utils.password_validation import validate_password_strength
            is_valid, errors = validate_password_strength(password)
            if not is_valid:
                error_message = 'New password does not meet requirements: ' + '; '.join(errors)
                raise ValueError(error_message)
            
            # Check password history to prevent reuse
            hashed_new_password = self.hash_password(password)
            if settings.PASSWORD_HISTORY_COUNT > 0:
                # Get recent password history
                recent_passwords = db.query(PasswordHistory).filter(
                    PasswordHistory.user_id == user.id
                ).order_by(PasswordHistory.created_at.desc()).limit(settings.PASSWORD_HISTORY_COUNT).all()
                
                # Check if new password matches any recent password
                for old_password in recent_passwords:
                    if self.verify_password(password, old_password.password_hash):
                        raise ValueError(f"Password cannot be reused. You must use a password that hasn't been used in your last {settings.PASSWORD_HISTORY_COUNT} password changes.")
            
            # Check if new password is same as current password
            if self.verify_password(password, user.password):
                raise ValueError("New password must be different from your current password")

            # SECURITY: Revoke all existing sessions and refresh tokens on password change
            # This prevents compromised sessions from remaining valid after password change
            from ..models.refresh_token import RefreshToken
            revoked_sessions = session_service.revoke_all_user_sessions(db, user.id)
            revoked_tokens = db.query(RefreshToken).filter(RefreshToken.user_id == user.id).delete()
            logger.info(f"Password change for user {user.id}: Revoked {revoked_sessions} sessions and {revoked_tokens} refresh tokens")
            
            # Save current password to history before changing
            if settings.PASSWORD_HISTORY_COUNT > 0:
                password_history_entry = PasswordHistory(
                    user_id=user.id,
                    password_hash=user.password
                )
                db.add(password_history_entry)
                
                # Clean up old password history (keep only the configured number)
                old_passwords = db.query(PasswordHistory).filter(
                    PasswordHistory.user_id == user.id
                ).order_by(PasswordHistory.created_at.desc()).offset(settings.PASSWORD_HISTORY_COUNT).all()
                for old_pwd in old_passwords:
                    db.delete(old_pwd)
            
            # Update password and timestamp
            user.password = hashed_new_password
            user.password_changed_at = datetime.utcnow()

        if full_name is not None:
            user.full_name = full_name
        email_verification_required = False
        verification_token = None
        
        if email is not None:
            # Normalize email (lowercase and trim)
            email = email.lower().strip()
            
            # If email is changing, require re-verification
            if email != user.email:
                existing_user = db.query(User).filter(
                    User.email == email,
                    User.id != user_id
                ).first()
                if existing_user:
                    raise ValueError("Email already in use")
                
                # Create email verification token for new email
                verification_token = secrets.token_hex(32)
                hashed_verification_token = hashlib.sha256(verification_token.encode()).hexdigest()
                expires_at = datetime.utcnow() + timedelta(hours=24)
                
                # Mark email as unverified until new email is verified
                user.email_verified = False
                email_verification_required = True
                
                # Delete old verification tokens for this user
                db.query(EmailVerificationToken).filter(EmailVerificationToken.user_id == user.id).delete()
                
                verification_token_obj = EmailVerificationToken(
                    user_id=user.id,
                    token=hashed_verification_token,
                    email=email,  # New email to verify
                    expires_at=expires_at
                )
                db.add(verification_token_obj)
                
                # Update email (but it's not verified yet)
                user.email = email
                
                # Send verification email to new address
                try:
                    client_url = settings.CLIENT_URL or os.getenv("CLIENT_URL", "http://localhost:5173")
                    verification_url = f"{client_url}/verify-email/{verification_token}"
                    email_html = email_changed_verification_template(verification_url, user.full_name, email)
                    await send_email(
                        to=email,
                        subject="Verify Your New Email Address",
                        html=email_html
                    )
                    logger.info(f"Email change verification sent to {email}")
                except Exception as e:
                    logger.error(f"Failed to send email change verification to {email}: {type(e).__name__}: {str(e)}", exc_info=True)
        
        if phone_number is not None:
            user.phone = phone_number
        if currency is not None:

            if len(currency) == 3 and currency.isalpha():
                user.currency = currency.upper()
            else:
                raise ValueError("Invalid currency code. Must be a 3-letter ISO 4217 code (e.g., USD, EUR, VND)")

        db.commit()
        db.refresh(user)

        user.role = db.query(Role).filter(Role.id == user.role_id).first()

        response = self.format_user_response(user)
        if email_verification_required:
            response["email_verification_required"] = True
            response["message"] = "Profile updated. Please verify your new email address. A verification link has been sent."
        
        return response

    def generate_reset_token(self) -> tuple:
        reset_token = secrets.token_hex(32)
        hashed_token = hashlib.sha256(reset_token.encode()).hexdigest()
        return reset_token, hashed_token

    async def forgot_password(self, db: Session, email: str) -> dict:

        user = db.query(User).filter(User.email == email).first()

        if not user:
            return {
                "success": True,
                "message": "If email exists, reset link has been sent"
            }

        reset_token, hashed_token = self.generate_reset_token()

        db.query(PasswordResetToken).filter(PasswordResetToken.user_id == user.id).delete()

        # SECURITY: Shorter expiration for password reset tokens (15 minutes instead of 1 hour)
        expires_at = datetime.utcnow() + timedelta(minutes=15)
        reset_token_obj = PasswordResetToken(
            user_id=user.id,
            token=hashed_token,
            expires_at=expires_at
        )
        db.add(reset_token_obj)
        db.commit()

        client_url = settings.CLIENT_URL or os.getenv("CLIENT_URL", "http://localhost:5173")
        reset_url = f"{client_url}/reset-password/{reset_token}"

        try:
            logger.info(f"Attempting to send password reset email to {user.email}")
            logger.info(f"Reset URL: {reset_url}")
            email_html = password_reset_email_template(reset_url)
            
            plain_text = f"Please click the following link to reset your password: {reset_url}\n\nIf you did not request this, please ignore this email."
            
            await send_email(
                to=user.email,
                subject="Reset password - Hotel Booking",
                html=email_html,
                text=plain_text
            )
            logger.info(f"Password reset email sent successfully to {user.email} with reset URL: {reset_url}")
        except Exception as e:
            logger.error(f"Failed to send password reset email to {user.email}: {type(e).__name__}: {str(e)}", exc_info=True)

        return {
            "success": True,
            "message": "Password reset link has been sent to your email"
        }

    async def reset_password(self, db: Session, token: str, password: str) -> dict:
        if not token or not password:
            raise ValueError("Token and password are required")

        hashed_token = hashlib.sha256(token.encode()).hexdigest()

        reset_token = db.query(PasswordResetToken).filter(
            PasswordResetToken.token == hashed_token,
            PasswordResetToken.expires_at > datetime.utcnow(),
            PasswordResetToken.used == False
        ).first()

        if not reset_token:
            raise ValueError("Invalid or expired reset token")

        user = db.query(User).filter(User.id == reset_token.user_id).first()
        if not user:
            raise ValueError("User not found")

        if self.verify_password(password, user.password):
            raise ValueError("New password must be different from the old password")

        # Check password history to prevent reuse (if enabled)
        hashed_new_password = self.hash_password(password)
        if settings.PASSWORD_HISTORY_COUNT > 0:
            # Get recent password history
            recent_passwords = db.query(PasswordHistory).filter(
                PasswordHistory.user_id == user.id
            ).order_by(PasswordHistory.created_at.desc()).limit(settings.PASSWORD_HISTORY_COUNT).all()
            
            # Check if new password matches any recent password
            for old_password in recent_passwords:
                if self.verify_password(password, old_password.password_hash):
                    raise ValueError(f"Password cannot be reused. You must use a password that hasn't been used in your last {settings.PASSWORD_HISTORY_COUNT} password changes.")
        
        # Save current password to history before changing
        if settings.PASSWORD_HISTORY_COUNT > 0:
            password_history_entry = PasswordHistory(
                user_id=user.id,
                password_hash=user.password
            )
            db.add(password_history_entry)
            
            # Clean up old password history (keep only the configured number)
            old_passwords = db.query(PasswordHistory).filter(
                PasswordHistory.user_id == user.id
            ).order_by(PasswordHistory.created_at.desc()).offset(settings.PASSWORD_HISTORY_COUNT).all()
            for old_pwd in old_passwords:
                db.delete(old_pwd)

        user.password = hashed_new_password
        user.password_changed_at = datetime.utcnow()
        
        # SECURITY: Revoke all existing sessions and refresh tokens on password reset
        # This prevents compromised sessions from remaining valid after password change
        from ..models.refresh_token import RefreshToken
        revoked_sessions = session_service.revoke_all_user_sessions(db, user.id)
        revoked_tokens = db.query(RefreshToken).filter(RefreshToken.user_id == user.id).delete()
        
        db.commit()

        reset_token.used = True
        db.commit()

        logger.info(f"Password reset for user {user.id}: Revoked {revoked_sessions} sessions and {revoked_tokens} refresh tokens")

        try:
            logger.info(f"Attempting to send password changed confirmation email to {user.email}")
            email_html = password_changed_email_template(user.email)
            await send_email(
                to=user.email,
                subject="Password Changed",
                html=email_html
            )
            logger.info(f"Password changed confirmation email sent successfully to {user.email}")
        except Exception as e:
            logger.error(f"Failed to send password changed confirmation email to {user.email}: {type(e).__name__}: {str(e)}", exc_info=True)

        return {
            "success": True,
            "message": "Password has been reset successfully. All existing sessions have been revoked for security."
        }

    async def verify_email(self, db: Session, token: str) -> dict:
        """Verify email address using verification token."""
        if not token:
            raise ValueError("Verification token is required")

        hashed_token = hashlib.sha256(token.encode()).hexdigest()

        verification_token = db.query(EmailVerificationToken).filter(
            EmailVerificationToken.token == hashed_token,
            EmailVerificationToken.expires_at > datetime.utcnow(),
            EmailVerificationToken.used == False
        ).first()

        if not verification_token:
            raise ValueError("Invalid or expired verification token")

        user = db.query(User).filter(User.id == verification_token.user_id).first()
        if not user:
            raise ValueError("User not found")

        # Update email if it was changed (verification token contains the new email)
        if verification_token.email != user.email:
            # Check if new email is already in use
            existing_user = db.query(User).filter(User.email == verification_token.email).first()
            if existing_user and existing_user.id != user.id:
                raise ValueError("Email address is already in use")
            user.email = verification_token.email

        # Mark email as verified
        user.email_verified = True
        verification_token.used = True
        
        # Delete all other verification tokens for this user
        db.query(EmailVerificationToken).filter(
            EmailVerificationToken.user_id == user.id,
            EmailVerificationToken.id != verification_token.id
        ).delete()
        
        db.commit()

        logger.info(f"Email verified for user {user.id}: {user.email}")

        return {
            "success": True,
            "message": "Email address verified successfully",
            "email": user.email
        }

    async def resend_verification_email(self, db: Session, user_id: int) -> dict:
        """Resend email verification link."""
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        if user.email_verified:
            raise ValueError("Email is already verified")

        # Delete old verification tokens
        db.query(EmailVerificationToken).filter(EmailVerificationToken.user_id == user.id).delete()

        # Create new verification token
        verification_token = secrets.token_hex(32)
        hashed_verification_token = hashlib.sha256(verification_token.encode()).hexdigest()
        expires_at = datetime.utcnow() + timedelta(hours=24)

        verification_token_obj = EmailVerificationToken(
            user_id=user.id,
            token=hashed_verification_token,
            email=user.email,
            expires_at=expires_at
        )
        db.add(verification_token_obj)
        db.commit()

        # Send verification email
        try:
            client_url = settings.CLIENT_URL or os.getenv("CLIENT_URL", "http://localhost:5173")
            verification_url = f"{client_url}/verify-email/{verification_token}"
            email_html = email_verification_template(verification_url, user.full_name)
            await send_email(
                to=user.email,
                subject="Verify Your Email Address",
                html=email_html
            )
            logger.info(f"Verification email resent to {user.email}")
        except Exception as e:
            logger.error(f"Failed to resend verification email to {user.email}: {type(e).__name__}: {str(e)}", exc_info=True)
            raise ValueError("Failed to send verification email. Please try again later.")

        return {
            "success": True,
            "message": "Verification email sent successfully"
        }

auth_service = AuthService()

