"""
Refactored Auth Service with proper separation of concerns
Focuses only on user management, delegates payment to tenant service
"""

import logging
from typing import Dict, Any, Optional
from datetime import datetime
from uuid import UUID
from fastapi import HTTPException, status
from shared.clients.tenant_client import TenantServiceClient
from app.core.config import settings
from shared.exceptions.auth_exceptions import (
    UserCreationError,
    RegistrationError,
    PaymentOrchestrationError
)
from app.core.security import SecurityManager
from app.repositories.user_repository import UserRepository
from app.repositories.onboarding_repository import OnboardingRepository
from app.models.users import User
from app.schemas.auth import UserRegistration, UserLogin

# Configure logging
logger = logging.getLogger(__name__)


class AuthService:
    """
    Auth Service focused solely on user management
    Delegates all payment operations to Tenant Service
    """

    def __init__(self, database_manager=None):
        """Initialize auth service"""
        self.database_manager = database_manager
        self.tenant_client = TenantServiceClient(settings)
    
    async def register_user(self, user_data: UserRegistration) -> User:
        """
        Atomic: Create user record (ONLY after payment verification)

        Args:
            user_data: User registration data

        Returns:
            Created user

        Raises:
            UserCreationError: If user creation fails
        """
        try:
            logger.info(f"Creating user record, email={user_data.email}")

            # Create user in database
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)

                # Hash the password before storing
                from app.core.security import SecurityManager
                hashed_password = SecurityManager.hash_password(user_data.password)

                user = await user_repo.create_user({
                    "email": user_data.email,
                    "hashed_password": hashed_password,
                    "full_name": user_data.full_name,
                    "is_active": True,
                    "is_verified": False,
                    "role": user_data.role or "admin"
                })

            logger.info(f"User created successfully, user_id={user.id}, email={user.email}")

            return user

        except Exception as e:
            logger.error(f"User creation failed, error={str(e)}, email={user_data.email}", exc_info=True)
            raise UserCreationError(f"User creation failed: {str(e)}") from e
    
    # The save_onboarding_progress method has been integrated into complete_registration_after_payment_verification
    # to ensure proper transaction handling and avoid foreign key constraint violations
    # See complete_registration_after_payment_verification method for the implementation
    
    async def initiate_registration_payment_setup(
        self,
        user_data: UserRegistration
    ) -> Dict[str, Any]:
        """
        Initiate payment setup via tenant service orchestration
        This is the FIRST step in secure registration flow
        
        Args:
            user_data: User registration data
            
        Returns:
            Payment setup result from tenant service
            
        Raises:
            PaymentOrchestrationError: If payment setup fails
        """
        try:
            logger.info(f"Initiating registration payment setup via tenant service, email={user_data.email}, plan={user_data.subscription_plan}")
            
            # Prepare user data for tenant service
            user_data_for_tenant = {
                "email": user_data.email,
                "full_name": user_data.full_name,
                "payment_method_id": user_data.payment_method_id,
                "plan_id": user_data.subscription_plan or "professional",
                "billing_cycle": user_data.billing_cycle or "monthly",
                "coupon_code": user_data.coupon_code
            }
            
            # Call tenant service orchestration endpoint
            result = await self.tenant_client.create_registration_payment_setup(user_data_for_tenant)
            
            logger.info(f"Registration payment setup completed via tenant service, email={user_data.email}, requires_action={result.get('requires_action')}, setup_intent_id={result.get('setup_intent_id')}")
            
            return result
            
        except Exception as e:
            logger.error(f"Registration payment setup via tenant service failed, error={str(e)}, email={user_data.email}", exc_info=True)
            raise PaymentOrchestrationError(f"Payment setup failed: {str(e)}") from e
    
    async def complete_registration_after_payment_verification(
        self,
        setup_intent_id: Optional[str],
        user_data: UserRegistration,
        payment_setup_result: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Complete registration after successful payment verification.
        
        This is the CORE method that creates the subscription and user.
        It is called by complete_registration_with_verified_payment in both scenarios:
        1. After 3DS authentication (requires_action=True flow)
        2. Immediately after start-registration (requires_action=False flow)
        
        CRITICAL: This is the FIRST and ONLY place where:
        - Subscription is created via tenant service
        - User record is created in auth database
        - Onboarding progress is saved
        - Auth tokens are generated
        
        This ensures that subscriptions are only created after SetupIntent verification,
        preventing duplicate subscriptions and maintaining payment security.

        Args:
            setup_intent_id: Verified SetupIntent ID (must not be None)
            user_data: User registration data
            payment_setup_result: Optional payment setup result with customer_id etc.

        Returns:
            Complete registration result with:
            - user: Created user data
            - subscription_id: Created subscription ID
            - payment_customer_id: Stripe customer ID
            - status: Subscription status
            - access_token: JWT access token
            - refresh_token: JWT refresh token

        Raises:
            RegistrationError: If registration completion fails (e.g., missing setup_intent_id)
        """
        try:
            logger.info(f"Completing registration after verification, email={user_data.email}, setup_intent_id={setup_intent_id}")

            if not setup_intent_id:
                raise RegistrationError("SetupIntent ID is required for registration completion")

            # Get customer_id and other data from payment_setup_result
            customer_id = ""
            payment_method_id = ""
            trial_period_days = 0

            if payment_setup_result:
                customer_id = payment_setup_result.get('customer_id') or payment_setup_result.get('payment_customer_id', '')
                payment_method_id = payment_setup_result.get('payment_method_id', '')
                trial_period_days = payment_setup_result.get('trial_period_days', 0)

            # Call tenant service to verify SetupIntent and CREATE subscription
            subscription_result = await self.tenant_client.verify_and_complete_registration(
                setup_intent_id,
                {
                    "email": user_data.email,
                    "full_name": user_data.full_name,
                    "plan_id": user_data.subscription_plan or "professional",
                    "subscription_plan": user_data.subscription_plan or "professional",
                    "billing_cycle": user_data.billing_cycle or "monthly",
                    "billing_interval": user_data.billing_cycle or "monthly",
                    "coupon_code": user_data.coupon_code,
                    "customer_id": customer_id,
                    "payment_method_id": payment_method_id or user_data.payment_method_id,
                    "trial_period_days": trial_period_days
                }
            )

            # Use a single database session for both user creation and onboarding progress
            # to ensure proper transaction handling and avoid foreign key constraint violations
            async with self.database_manager.get_session() as session:
                # Step 2: Create user (ONLY after successful payment verification)
                user_repo = UserRepository(User, session)

                # Hash the password before storing
                from app.core.security import SecurityManager
                hashed_password = SecurityManager.hash_password(user_data.password)

                user = await user_repo.create_user({
                    "email": user_data.email,
                    "hashed_password": hashed_password,
                    "full_name": user_data.full_name,
                    "is_active": True,
                    "is_verified": False,
                    "role": user_data.role or "admin"
                })

                # Step 3: Save onboarding progress with subscription data using the same session
                onboarding_repo = OnboardingRepository(session)

                progress_data = {
                    'user_id': str(user.id),
                    'email': user_data.email,
                    'full_name': user_data.full_name,
                    'subscription_plan': user_data.subscription_plan,
                    'billing_cycle': user_data.billing_cycle,
                    'payment_customer_id': subscription_result.get('payment_customer_id'),
                    'subscription_id': subscription_result.get('subscription_id'),
                    'payment_method_id': user_data.payment_method_id,
                    'coupon_code': user_data.coupon_code,
                    'registration_completed': subscription_result.get('subscription_id') is not None,
                    'timestamp': datetime.now().isoformat()
                }

                # Save user registration step using the same session
                await onboarding_repo.upsert_user_step(
                    user_id=str(user.id),
                    step_name="user_registered",
                    completed=True,
                    step_data=progress_data
                )

                # Commit the transaction to ensure both records are persisted
                await session.commit()

            logger.info(f"Registration completed successfully, user_id={user.id}, email={user.email}, subscription_id={subscription_result.get('subscription_id')}")

            # Generate auth tokens for auto-login after registration
            tokens = await self._generate_auth_tokens(user)

            return {
                'user': user,
                'subscription_id': subscription_result.get('subscription_id'),
                'payment_customer_id': subscription_result.get('payment_customer_id'),
                'status': subscription_result.get('status'),
                'access_token': tokens.get('access_token'),
                'refresh_token': tokens.get('refresh_token'),
                'message': 'Registration completed successfully'
            }

        except Exception as e:
            logger.error(f"Registration completion after payment verification failed, error={str(e)}, email={user_data.email}, setup_intent_id={setup_intent_id}", exc_info=True)
            raise RegistrationError(f"Registration completion failed: {str(e)}") from e
    
    async def start_secure_registration_flow(
        self,
        user_data: UserRegistration
    ) -> Dict[str, Any]:
        """
        Start secure registration flow with SetupIntent-first approach
        
        This is the FIRST step in the atomic registration flow:
        1. Creates Stripe customer
        2. Creates SetupIntent with confirm=True
        3. Returns SetupIntent data to frontend
        
        IMPORTANT: NO subscription is created in this step!
        Subscription creation happens in complete_registration_after_payment_verification
        
        Two possible outcomes:
        - requires_action=True: 3DS required, frontend must confirm SetupIntent
        - requires_action=False: No 3DS required, but frontend STILL must call complete-registration
        
        Args:
            user_data: User registration data
            
        Returns:
            Registration flow result with SetupIntent data
            - requires_action: True if 3DS required, False if not
            - setup_intent_id: SetupIntent ID for verification
            - client_secret: For 3DS authentication (when requires_action=True)
            - Other SetupIntent metadata
            
        Raises:
            RegistrationError: If registration flow fails
        """
        try:
            logger.info(f"Starting secure registration flow, email={user_data.email}, plan={user_data.subscription_plan}")
            
            # Step 1: Initiate payment setup via tenant service
            payment_setup_result = await self.initiate_registration_payment_setup(user_data)
            
            # Check if SetupIntent requires action (3DS)
            if payment_setup_result.get('requires_action', False):
                logger.info(f"Registration requires SetupIntent confirmation (3DS), email={user_data.email}, setup_intent_id={payment_setup_result.get('setup_intent_id')}")

                # Return SetupIntent for frontend to handle 3DS
                # Note: NO subscription exists yet - subscription is created after verification
                return {
                    'requires_action': True,
                    'action_type': 'setup_intent_confirmation',
                    'client_secret': payment_setup_result.get('client_secret'),
                    'setup_intent_id': payment_setup_result.get('setup_intent_id'),
                    'customer_id': payment_setup_result.get('customer_id'),
                    'payment_customer_id': payment_setup_result.get('payment_customer_id'),
                    'plan_id': payment_setup_result.get('plan_id'),
                    'payment_method_id': payment_setup_result.get('payment_method_id'),
                    'billing_cycle': payment_setup_result.get('billing_cycle'),
                    'trial_period_days': payment_setup_result.get('trial_period_days', 0),
                    'coupon_code': payment_setup_result.get('coupon_code'),
                    'email': payment_setup_result.get('email'),
                    'message': 'Payment verification required. Frontend must confirm SetupIntent.'
                }
            else:
                # No 3DS required - SetupIntent already succeeded
                logger.info(f"Registration SetupIntent succeeded without 3DS, email={user_data.email}, setup_intent_id={payment_setup_result.get('setup_intent_id')}")

                # Note: NO subscription created yet - frontend must call complete-registration
                # This ensures consistent flow whether 3DS is required or not
                return {
                    'requires_action': False,
                    'setup_intent_id': payment_setup_result.get('setup_intent_id'),
                    'customer_id': payment_setup_result.get('customer_id'),
                    'payment_customer_id': payment_setup_result.get('payment_customer_id'),
                    'plan_id': payment_setup_result.get('plan_id'),
                    'payment_method_id': payment_setup_result.get('payment_method_id'),
                    'billing_cycle': payment_setup_result.get('billing_cycle'),
                    'trial_period_days': payment_setup_result.get('trial_period_days', 0),
                    'coupon_code': payment_setup_result.get('coupon_code'),
                    'email': payment_setup_result.get('email'),
                    'message': 'SetupIntent succeeded without 3DS. Frontend must call complete-registration to create subscription.'
                }
                
        except Exception as e:
            logger.error(f"Secure registration flow failed, error={str(e)}, email={user_data.email}", exc_info=True)
            raise RegistrationError(f"Secure registration flow failed: {str(e)}") from e
    
    async def complete_registration_with_verified_payment(
        self,
        setup_intent_id: str,
        user_data: UserRegistration
    ) -> Dict[str, Any]:
        """
        Complete registration after frontend confirms SetupIntent
        
        This is the SECOND step in the atomic registration flow and is called in TWO scenarios:
        1. After user completes 3DS authentication (when requires_action=True)
        2. Immediately after start-registration (when requires_action=False)
        
        In BOTH cases, this method:
        - Verifies the SetupIntent status with Stripe
        - Calls tenant service to create the subscription (first time subscription is created)
        - Creates the user record in auth database
        - Saves onboarding progress
        - Generates auth tokens for auto-login
        
        This ensures a CONSISTENT flow whether 3DS is required or not:
        Step 1: start-registration creates SetupIntent only (NO subscription yet)
        Step 2: complete-registration verifies SetupIntent and creates subscription
        
        Args:
            setup_intent_id: SetupIntent ID that was confirmed (either by Stripe or frontend)
            user_data: User registration data
            
        Returns:
            Complete registration result with user data, tokens, and subscription info
            
        Raises:
            RegistrationError: If completion fails
        """
        try:
            logger.info(f"Completing registration after SetupIntent verification, email={user_data.email}, setup_intent_id={setup_intent_id}")
            
            # Complete registration after payment verification
            result = await self.complete_registration_after_payment_verification(
                setup_intent_id,
                user_data
            )
            
            logger.info(f"Registration completed successfully after 3DS, user_id={result['user'].id}, email={result['user'].email}, subscription_id={result['subscription_id']}")
            
            return {
                'success': True,
                'user': result['user'],
                'subscription_id': result['subscription_id'],
                'payment_customer_id': result['payment_customer_id'],
                'status': result['status'],
                'access_token': result.get('access_token'),
                'refresh_token': result.get('refresh_token'),
                'message': 'Registration completed successfully after 3DS verification'
            }
            
        except Exception as e:
            logger.error(f"Registration completion after 3DS failed, error={str(e)}, email={user_data.email}, setup_intent_id={setup_intent_id}", exc_info=True)
            raise RegistrationError(f"Registration completion after 3DS failed: {str(e)}") from e

    async def login_user(self, login_data: UserLogin) -> Dict[str, Any]:
        """
        Perform user login with subscription validation

        This method:
        1. Validates user credentials
        2. Checks if user has active subscription (if required)
        3. Generates authentication tokens
        4. Updates last login timestamp

        Args:
            login_data: User login credentials (email and password)

        Returns:
            Dictionary with user information, tokens, and subscription details

        Raises:
            HTTPException: 401 for invalid credentials, 403 for subscription issues
        """
        try:
            logger.info(f"Processing login request, email={login_data.email}")

            # Validate user credentials using repository
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)

                # Authenticate user
                user = await user_repo.authenticate_user(login_data.email, login_data.password)

                if not user:
                    logger.warning(f"Login failed - user not found or invalid password, email={login_data.email}")
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Invalid credentials"
                    )

                # Check if user is active
                if not user.is_active:
                    logger.warning(f"Login failed - inactive user, email={login_data.email}, user_id={user.id}")
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Account is inactive"
                    )

                # Validate subscription (if required)
                subscription_valid = await self._validate_user_subscription(user)

                if not subscription_valid:
                    logger.warning(f"Login failed - invalid subscription, email={login_data.email}, user_id={user.id}")
                    raise HTTPException(
                        status_code=status.HTTP_403_FORBIDDEN,
                        detail="Subscription required or expired"
                    )

                # Update last login timestamp
                await user_repo.update_last_login(str(user.id))

                # Get subscription details
                subscription_details = await self._get_user_subscription_details(user)

                logger.info(f"Login successful, email={login_data.email}, user_id={user.id}")

                # Generate authentication tokens
                tokens = await self._generate_auth_tokens(user)

                return {
                    "user": user,
                    "tokens": tokens,
                    "subscription": subscription_details
                }

        except HTTPException:
            # Re-raise HTTP exceptions
            raise
        except Exception as e:
            logger.error(f"Login processing failed, error={str(e)}, email={login_data.email}", exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Login failed: {str(e)}"
            ) from e
    
    async def _validate_user_subscription(self, user: User) -> bool:
        """
        Validate user subscription status
        
        Args:
            user: User object
            
        Returns:
            True if subscription is valid, False otherwise
        """
        try:
            logger.info(f"Validating user subscription, user_id={user.id}, email={user.email}")
            
            # Since tenant relationships are managed by tenant service, we need to call tenant service
            # to get the user's primary tenant and validate subscription status
            
            # Get user's primary tenant from tenant service
            primary_tenant = await self.tenant_client.get_user_primary_tenant(str(user.id))
            
            if primary_tenant:
                tenant_id = primary_tenant.get('tenant_id')
                logger.info(f"User has primary tenant - subscription validation required, user_id={user.id}, tenant_id={tenant_id}")
                
                # Call tenant service to validate subscription status
                subscription_status = await self.tenant_client.get_subscription_status(tenant_id)
                
                if subscription_status:
                    status = subscription_status.get('status', 'unknown')
                    
                    logger.info(f"Subscription status retrieved from tenant service, user_id={user.id}, tenant_id={tenant_id}, status={status}")
                    
                    # Consider subscription valid if it's active, trialing, or in grace period
                    valid_statuses = ['active', 'trialing', 'grace_period']
                    return status in valid_statuses
                else:
                    logger.warning(f"No subscription status returned from tenant service, user_id={user.id}, tenant_id={tenant_id}")
                    return False
            else:
                # Users without primary tenant might be in registration flow or using free tier
                logger.info(f"User without primary tenant - no subscription validation required, user_id={user.id}")
                return True
                
        except Exception as e:
            logger.error(f"Subscription validation failed, error={str(e)}, user_id={user.id}, email={user.email}", exc_info=True)
            # If validation fails, assume subscription is invalid
            return False
    
    async def _get_user_subscription_details(self, user: User) -> Dict[str, Any]:
        """
        Get user subscription details
        
        Args:
            user: User object
            
        Returns:
            Dictionary with subscription details
        """
        try:
            logger.info(f"Getting subscription details, user_id={user.id}")
            
            # Get user's primary tenant from tenant service
            primary_tenant = await self.tenant_client.get_user_primary_tenant(str(user.id))
            
            if primary_tenant:
                tenant_id = primary_tenant.get('tenant_id')
                
                # Call tenant service to get subscription details
                subscription = await self.tenant_client.get_subscription_details(tenant_id)
                
                if subscription:
                    logger.info(f"Subscription details retrieved from tenant service, user_id={user.id}, tenant_id={tenant_id}, plan={subscription.get('plan')}, status={subscription.get('status')}")
                    # Add tenant_id to subscription details for JWT
                    subscription['tenant_id'] = tenant_id
                    return subscription
                else:
                    logger.warning(f"No subscription details returned from tenant service, user_id={user.id}, tenant_id={tenant_id}")
                    return {
                        "status": "no_subscription",
                        "plan": None,
                        "billing_cycle": None,
                        "current_period_end": None,
                        "trial_period_days": 0,
                        "tenant_id": tenant_id
                    }
            else:
                logger.info(f"User without primary tenant - no subscription details available, user_id={user.id}")
                return {
                    "status": "no_tenant",
                    "plan": None,
                    "billing_cycle": None,
                    "current_period_end": None,
                    "trial_period_days": 0
                }
                
        except Exception as e:
            logger.error(f"Failed to get subscription details, error={str(e)}, user_id={user.id}", exc_info=True)
            return {
                "status": "unknown",
                "plan": None,
                "billing_cycle": None,
                "current_period_end": None,
                "trial_period_days": 0
            }

    async def get_user_by_id(self, user_id: str) -> Optional[User]:
        """
        Get user by ID

        Args:
            user_id: User ID to retrieve

        Returns:
            User object if found, None otherwise
        """
        try:
            logger.info(f"Getting user by ID, user_id={user_id}")

            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                user = await user_repo.get_by_id(user_id)

            if user:
                logger.info(f"User retrieved successfully, user_id={user_id}, email={user.email}")
            else:
                logger.warning(f"User not found, user_id={user_id}")

            return user

        except Exception as e:
            logger.error(f"Get user by ID failed, error={str(e)}, user_id={user_id}", exc_info=True)
            raise

    async def _generate_auth_tokens(self, user: User) -> Dict[str, Any]:
        """
        Generate authentication tokens for user
        
        Args:
            user: User object
            
        Returns:
            Dictionary with access and refresh tokens
        """
        try:
            logger.info(f"Generating auth tokens, user_id={user.id}")
            
            # Get subscription details for JWT payload
            subscription_details = await self._get_user_subscription_details(user)
            
            # Prepare access token payload
            access_token_data = {
                "user_id": str(user.id),
                "email": user.email,
                "full_name": user.full_name,
                "is_verified": user.is_verified,
                "is_active": user.is_active,
                "role": user.role or "admin",
                "type": "access"
            }
            
            # Add subscription data to access token
            if subscription_details:
                access_token_data["subscription"] = subscription_details
                if subscription_details.get("tenant_id"):
                    access_token_data["tenant_id"] = subscription_details["tenant_id"]
            
            # Generate access token using SecurityManager
            access_token = SecurityManager.create_access_token(access_token_data)
            
            # Prepare refresh token payload
            refresh_token_data = {
                "user_id": str(user.id),
                "email": user.email,
                "type": "refresh"
            }
            
            # Generate refresh token using SecurityManager
            refresh_token = SecurityManager.create_refresh_token(refresh_token_data)
            
            # ✅ CRITICAL FIX: Store refresh token in Redis for later validation
            await SecurityManager.store_refresh_token(str(user.id), refresh_token)
            
            logger.info(f"Auth tokens generated successfully, user_id={user.id}, access_token_length={len(access_token)}, refresh_token_length={len(refresh_token)}")
            
            return {
                "access_token": access_token,
                "refresh_token": refresh_token,
                "expires_in": 3600,
                "token_type": "bearer"
            }
            
        except Exception as e:
            logger.error(f"Token generation failed, error={str(e)}, user_id={user.id}",
                        exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Token generation failed: {str(e)}"
            ) from e

    async def refresh_auth_tokens(self, refresh_token: str) -> Dict[str, Any]:
        """
        Refresh authentication tokens using a valid refresh token
        
        Args:
            refresh_token: Valid refresh token
            
        Returns:
            Dictionary with new access and refresh tokens
            
        Raises:
            HTTPException: 401 for invalid refresh tokens
        """
        try:
            logger.info("Refreshing auth tokens using refresh token")
            
            # Import JWT dependencies
            from jose import jwt, JWTError
            from app.core.config import settings
            from app.core.security import SecurityManager
            
            # Decode refresh token to get user info
            payload = jwt.decode(
                refresh_token,
                settings.JWT_SECRET_KEY,
                algorithms=[settings.JWT_ALGORITHM]
            )
            
            # Verify this is a refresh token
            if payload.get("type") != "refresh":
                logger.warning("Invalid token type for refresh")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid token type"
                )
            
            user_id = payload.get("user_id")
            email = payload.get("email")
            
            if not user_id or not email:
                logger.warning("Invalid refresh token payload")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid refresh token"
                )
            
            # Check if refresh token is valid in Redis using SecurityManager
            is_valid = await SecurityManager.is_refresh_token_valid(user_id, refresh_token)
            if not is_valid:
                logger.warning(f"Invalid or expired refresh token for user {user_id}")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid or expired refresh token"
                )
            
            logger.info(f"Refresh token validated for user {user_id}, email={email}")
            
            # Get user from database
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                user = await user_repo.get_by_id(user_id)
                
                if not user:
                    logger.warning(f"User not found for refresh token, user_id={user_id}")
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="User not found"
                    )
                
                if not user.is_active:
                    logger.warning(f"Inactive user attempted token refresh, user_id={user_id}")
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Account is inactive"
                    )
                
                # Generate new tokens using existing method
                tokens = await self._generate_auth_tokens(user)
                
                logger.info(f"Token refresh successful, user_id={user_id}, email={email}")
                
                return tokens
                
        except JWTError as e:
            logger.warning(f"JWT decode error during refresh: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid refresh token"
            )
        except HTTPException:
            # Re-raise HTTP exceptions
            raise
        except Exception as e:
            logger.error(f"Token refresh failed: {str(e)}", exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Token refresh failed: {str(e)}"
            ) from e

    async def verify_access_token(self, access_token: str) -> Dict[str, Any]:
        """
        Verify the validity of an access token
        
        Args:
            access_token: Access token to verify
            
        Returns:
            Dictionary with token validation result and user info
            
        Raises:
            HTTPException: 401 for invalid access tokens
        """
        try:
            logger.info("Verifying access token")
            
            # Import JWT dependencies
            from jose import jwt, JWTError
            from app.core.config import settings
            
            # Decode and verify token
            payload = jwt.decode(
                access_token,
                settings.JWT_SECRET_KEY,
                algorithms=[settings.JWT_ALGORITHM]
            )
            
            # Verify this is an access token
            if payload.get("type") != "access":
                logger.warning("Invalid token type for verification")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid token type"
                )
            
            user_id = payload.get("user_id")
            email = payload.get("email")
            
            if not user_id or not email:
                logger.warning("Invalid access token payload")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid access token"
                )
            
            logger.info(f"Token verification successful, user_id={user_id}, email={email}")
            
            return {
                "valid": True,
                "user_id": user_id,
                "email": email
            }
            
        except JWTError as e:
            logger.warning(f"JWT decode error during verification: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid access token"
            )
        except HTTPException:
            # Re-raise HTTP exceptions
            raise
        except Exception as e:
            logger.error(f"Token verification failed: {str(e)}", exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Token verification failed: {str(e)}"
            ) from e

    async def revoke_refresh_token(self, refresh_token: str) -> bool:
        """
        Revoke a refresh token
        
        Args:
            refresh_token: Refresh token to revoke
            
        Returns:
            True if revocation was successful or token didn't exist
            
        Raises:
            Exception: For unexpected errors
        """
        try:
            logger.info("Revoking refresh token")
            
            # Try to decode token to get user_id
            from jose import jwt, JWTError
            from app.core.config import settings
            
            user_id = "unknown"
            try:
                payload = jwt.decode(
                    refresh_token,
                    settings.JWT_SECRET_KEY,
                    algorithms=[settings.JWT_ALGORITHM]
                )
                user_id = payload.get("user_id") or "unknown"
            except JWTError as e:
                logger.warning(f"Could not decode refresh token during revocation: {str(e)}")
            
            # Revoke the token using SecurityManager
            await SecurityManager.revoke_refresh_token(user_id, refresh_token)
            logger.info(f"Refresh token revoked successfully, user_id={user_id}")
            
            return True
            
        except Exception as e:
            logger.error(f"Error revoking refresh token: {str(e)}", exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Token revocation failed: {str(e)}"
            ) from e

    async def change_user_password(self, user_id: str, current_password: str, new_password: str) -> bool:
        """
        Change user password
        
        Args:
            user_id: User ID
            current_password: Current password for verification
            new_password: New password to set
            
        Returns:
            True if password was changed successfully
            
        Raises:
            HTTPException: 401 for invalid current password, 404 for user not found
        """
        try:
            logger.info(f"Changing password for user {user_id}")
            
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                # Get current user
                user = await user_repo.get_by_id(user_id)
                if not user:
                    logger.warning(f"User not found for password change, user_id={user_id}")
                    raise HTTPException(
                        status_code=status.HTTP_404_NOT_FOUND,
                        detail="User not found"
                    )
                
                # Verify current password
                from app.core.security import verify_password
                is_valid = verify_password(current_password, user.hashed_password)
                
                if not is_valid:
                    logger.warning(f"Invalid current password for user {user_id}")
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Current password is incorrect"
                    )
                
                # Update password
                await user_repo.update_password(user_id, new_password)
                
                logger.info(f"Password change successful, user_id={user_id}, email={user.email}")
                
                return True
                
        except HTTPException:
            # Re-raise HTTP exceptions
            raise
        except Exception as e:
            logger.error(f"Password change failed: {str(e)}", exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Password change failed: {str(e)}"
            ) from e

    async def verify_user_email(self, email: str, verification_token: str) -> bool:
        """
        Verify user email address
        
        Args:
            email: User email
            verification_token: Email verification token
            
        Returns:
            True if email was verified successfully
            
        Raises:
            HTTPException: 404 for user not found
        """
        try:
            logger.info(f"Verifying email for {email}")
            
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                # Find user by email
                user = await user_repo.get_by_email(email)
                if not user:
                    logger.warning(f"User not found for email verification, email={email}")
                    raise HTTPException(
                        status_code=status.HTTP_404_NOT_FOUND,
                        detail="User not found"
                    )
                
                # TODO: Implement actual email verification token logic
                # For now, just mark as verified
                await user_repo.update_user(user.id, {"is_verified": True})
                
                logger.info(f"Email verification successful, user_id={user.id}, email={email}")
                
                return True
                
        except HTTPException:
            # Re-raise HTTP exceptions
            raise
        except Exception as e:
            logger.error(f"Email verification failed: {str(e)}", exc_info=True)
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Email verification failed: {str(e)}"
            ) from e

    async def request_password_reset(self, email: str) -> bool:
        """
        Request a password reset for a user

        Args:
            email: User's email address

        Returns:
            True if request was processed (whether user exists or not)
        """
        try:
            logger.info(f"Processing password reset request for email: {email}")

            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)

                # Find user by email (don't reveal if user exists)
                user = await user_repo.get_by_field("email", email)

                if not user:
                    logger.info(f"Password reset request for non-existent email: {email}")
                    return True  # Don't reveal if email exists

                # Generate reset token
                reset_token = SecurityManager.generate_reset_token()

                # Set expiration (1 hour)
                from datetime import datetime, timedelta, timezone
                expires_at = datetime.now(timezone.utc) + timedelta(hours=1)

                # Store reset token
                token_repo = PasswordResetTokenRepository(session)

                # Clean up expired tokens
                await token_repo.cleanup_expired_tokens()

                # Create new token
                await token_repo.create_token(
                    user_id=str(user.id),
                    token=reset_token,
                    expires_at=expires_at
                )

                # Commit transaction
                await session.commit()

                logger.info(f"Password reset token created for user: {email}")

                return True

        except Exception as e:
            logger.error(f"Password reset request failed: {str(e)}", exc_info=True)
            return False

    async def reset_password_with_token(self, token: str, new_password: str) -> bool:
        """
        Reset password using a valid reset token

        Args:
            token: Password reset token
            new_password: New password to set

        Returns:
            True if password was successfully reset
        """
        try:
            logger.info(f"Processing password reset with token: {token[:10]}...")

            # Validate password strength
            if not SecurityManager.validate_password(new_password):
                errors = SecurityManager.get_password_validation_errors(new_password)
                logger.warning(f"Password validation failed: {errors}")
                raise ValueError(f"Password does not meet requirements: {'; '.join(errors)}")

            async with self.database_manager.get_session() as session:
                # Find the reset token
                token_repo = PasswordResetTokenRepository(session)
                reset_token_obj = await token_repo.get_token_by_value(token)

                if not reset_token_obj:
                    logger.warning(f"Invalid or expired password reset token: {token[:10]}...")
                    raise ValueError("Invalid or expired reset token")

                # Get the user
                user_repo = UserRepository(User, session)
                user = await user_repo.get_by_id(str(reset_token_obj.user_id))

                if not user:
                    logger.error(f"User not found for reset token: {token[:10]}...")
                    raise ValueError("Invalid reset token")

                # Hash the new password
                hashed_password = SecurityManager.hash_password(new_password)

                # Update user's password
                await user_repo.update(str(user.id), {
                    "hashed_password": hashed_password
                })

                # Mark token as used
                await token_repo.mark_token_as_used(str(reset_token_obj.id))

                # Commit transaction
                await session.commit()

                logger.info(f"Password successfully reset for user: {user.email}")

                return True

        except ValueError:
            # Re-raise value errors (validation errors)
            raise
        except Exception as e:
            logger.error(f"Password reset failed: {str(e)}", exc_info=True)
            return False

# Import database manager for singleton instance
from app.core.database import database_manager

# Import required modules for the password reset functionality
from app.repositories.password_reset_repository import PasswordResetTokenRepository
from shared.clients.notification_client import NotificationServiceClient

# Singleton instance for dependency injection
auth_service = AuthService(database_manager=database_manager)


class EnhancedAuthService(AuthService):
    """
    Enhanced Auth Service with additional functionality for testing and advanced operations
    """

    async def _get_service_token(self) -> str:
        """
        Get a service token for internal service communication
        """
        from app.core.security import SecurityManager
        return SecurityManager.create_service_token("auth-service")