"""
Enhanced User Service
Updated to use repository pattern with dependency injection and improved error handling
"""

from datetime import datetime, timezone
from typing import Dict, Any, List, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import HTTPException, status
import structlog

from app.repositories import UserRepository, TokenRepository
from app.schemas.auth import UserResponse
from app.schemas.users import UserUpdate
from app.models.users import User
from app.models.tokens import RefreshToken
from app.core.security import SecurityManager
from shared.database.unit_of_work import UnitOfWork
from shared.database.transactions import transactional
from shared.database.exceptions import DatabaseError, ValidationError

logger = structlog.get_logger()


class EnhancedUserService:
    """Enhanced user management service using repository pattern"""
    
    def __init__(self, database_manager):
        """Initialize service with database manager"""
        self.database_manager = database_manager
    
    async def get_user_by_id(self, user_id: str, session: Optional[AsyncSession] = None) -> Optional[UserResponse]:
        """Get user by ID using repository pattern"""
        try:
            if session:
                # Use provided session (for direct session injection)
                user_repo = UserRepository(User, session)
                user = await user_repo.get_by_id(user_id)
            else:
                # Use database manager to get session
                async with self.database_manager.get_session() as db_session:
                    user_repo = UserRepository(User, db_session)
                    user = await user_repo.get_by_id(user_id)

            if not user:
                return None

            return UserResponse(
                id=str(user.id),
                email=user.email,
                full_name=user.full_name,
                is_active=user.is_active,
                is_verified=user.is_verified,
                created_at=user.created_at,
                role=user.role,
                phone=getattr(user, 'phone', None),
                language=getattr(user, 'language', None),
                timezone=getattr(user, 'timezone', None)
            )

        except Exception as e:
            logger.error("Failed to get user by ID using repository pattern",
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to get user: {str(e)}")
    
    async def get_user_by_email(self, email: str) -> Optional[UserResponse]:
        """Get user by email using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                user = await user_repo.get_by_email(email)
                if not user:
                    return None
                
                return UserResponse(
                    id=str(user.id),
                    email=user.email,
                    full_name=user.full_name,
                    is_active=user.is_active,
                    is_verified=user.is_verified,
                    created_at=user.created_at,
                    role=user.role,
                    phone=getattr(user, 'phone', None),
                    language=getattr(user, 'language', None),
                    timezone=getattr(user, 'timezone', None)
                )
                
        except Exception as e:
            logger.error("Failed to get user by email using repository pattern",
                        email=email,
                        error=str(e))
            raise DatabaseError(f"Failed to get user: {str(e)}")
    
    async def get_users_list(
        self,
        skip: int = 0,
        limit: int = 100,
        active_only: bool = True,
        role: str = None
    ) -> List[UserResponse]:
        """Get paginated list of users using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                filters = {}
                if active_only:
                    filters["is_active"] = True
                if role:
                    filters["role"] = role
                
                users = await user_repo.get_multi(
                    filters=filters,
                    skip=skip,
                    limit=limit,
                    order_by="created_at",
                    order_desc=True
                )
                
                return [
                    UserResponse(
                        id=str(user.id),
                        email=user.email,
                        full_name=user.full_name,
                        is_active=user.is_active,
                        is_verified=user.is_verified,
                        created_at=user.created_at,
                        role=user.role,
                        phone=getattr(user, 'phone', None),
                        language=getattr(user, 'language', None),
                        timezone=getattr(user, 'timezone', None)
                    )
                    for user in users
                ]
                
        except Exception as e:
            logger.error("Failed to get users list using repository pattern", error=str(e))
            return []
    
    @transactional
    async def update_user(
        self, 
        user_id: str, 
        user_data: UserUpdate, 
        session=None
    ) -> Optional[UserResponse]:
        """Update user information using repository pattern"""
        try:
            async with self.database_manager.get_session() as db_session:
                user_repo = UserRepository(User, db_session)
                
                # Validate user exists
                existing_user = await user_repo.get_by_id(user_id)
                if not existing_user:
                    raise HTTPException(
                        status_code=status.HTTP_404_NOT_FOUND,
                        detail="User not found"
                    )
                
                # Prepare update data
                update_data = {}
                if user_data.full_name is not None:
                    update_data["full_name"] = user_data.full_name
                if user_data.phone is not None:
                    update_data["phone"] = user_data.phone
                if user_data.language is not None:
                    update_data["language"] = user_data.language
                if user_data.timezone is not None:
                    update_data["timezone"] = user_data.timezone
                
                if not update_data:
                    # No updates to apply
                    return UserResponse(
                        id=str(existing_user.id),
                        email=existing_user.email,
                        full_name=existing_user.full_name,
                        is_active=existing_user.is_active,
                        is_verified=existing_user.is_verified,
                        created_at=existing_user.created_at,
                        role=existing_user.role
                    )
                
                # Update user using repository
                updated_user = await user_repo.update(user_id, update_data)
                if not updated_user:
                    raise DatabaseError("Failed to update user")
                
                logger.info("User updated successfully using repository pattern",
                           user_id=user_id,
                           updated_fields=list(update_data.keys()))
                
                return UserResponse(
                    id=str(updated_user.id),
                    email=updated_user.email,
                    full_name=updated_user.full_name,
                    is_active=updated_user.is_active,
                    is_verified=updated_user.is_verified,
                    created_at=updated_user.created_at,
                    role=updated_user.role,
                    phone=getattr(updated_user, 'phone', None),
                    language=getattr(updated_user, 'language', None),
                    timezone=getattr(updated_user, 'timezone', None)
                )
                
        except HTTPException:
            raise
        except Exception as e:
            logger.error("Failed to update user using repository pattern",
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to update user: {str(e)}")
    
    @transactional
    async def change_password(
        self,
        user_id: str,
        current_password: str,
        new_password: str,
        session=None
    ) -> bool:
        """Change user password using repository pattern"""
        try:
            async with self.database_manager.get_session() as db_session:
                async with UnitOfWork(db_session) as uow:
                    # Register repositories
                    user_repo = uow.register_repository("users", UserRepository)
                    token_repo = uow.register_repository("tokens", TokenRepository)
                    
                    # Get user and verify current password
                    user = await user_repo.get_by_id(user_id)
                    if not user:
                        raise HTTPException(
                            status_code=status.HTTP_404_NOT_FOUND,
                            detail="User not found"
                        )
                    
                    if not SecurityManager.verify_password(current_password, user.hashed_password):
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail="Current password is incorrect"
                        )
                    
                    # Hash new password and update
                    new_hashed_password = SecurityManager.hash_password(new_password)
                    await user_repo.update(user_id, {"hashed_password": new_hashed_password})
                    
                    # Revoke all existing tokens for security
                    await token_repo.revoke_user_tokens(user_id)
                    
                    # Commit transaction
                    await uow.commit()
                    
                    logger.info("Password changed successfully using repository pattern",
                               user_id=user_id)
                    
                    return True
                    
        except HTTPException:
            raise
        except Exception as e:
            logger.error("Failed to change password using repository pattern",
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to change password: {str(e)}")
    
    @transactional
    async def deactivate_user(self, user_id: str, admin_user_id: str, session=None) -> bool:
        """Deactivate user account using repository pattern"""
        try:
            async with self.database_manager.get_session() as db_session:
                async with UnitOfWork(db_session) as uow:
                    # Register repositories
                    user_repo = uow.register_repository("users", UserRepository)
                    token_repo = uow.register_repository("tokens", TokenRepository)
                    
                    # Verify user exists
                    user = await user_repo.get_by_id(user_id)
                    if not user:
                        return False
                    
                    # Update user status (soft delete)
                    updated_user = await user_repo.update(user_id, {"is_active": False})
                    if not updated_user:
                        return False
                    
                    # Revoke all tokens
                    await token_repo.revoke_user_tokens(user_id)
                    
                    # Commit transaction
                    await uow.commit()
                    
                    logger.info("User deactivated successfully using repository pattern",
                               user_id=user_id,
                               admin_user_id=admin_user_id)
                    
                    return True
                    
        except Exception as e:
            logger.error("Failed to deactivate user using repository pattern",
                        user_id=user_id,
                        error=str(e))
            return False
    
    @transactional
    async def activate_user(self, user_id: str, admin_user_id: str, session=None) -> bool:
        """Activate user account using repository pattern"""
        try:
            async with self.database_manager.get_session() as db_session:
                user_repo = UserRepository(User, db_session)
                
                # Update user status
                updated_user = await user_repo.update(user_id, {"is_active": True})
                if not updated_user:
                    return False
                
                logger.info("User activated successfully using repository pattern",
                           user_id=user_id,
                           admin_user_id=admin_user_id)
                
                return True
                
        except Exception as e:
            logger.error("Failed to activate user using repository pattern",
                        user_id=user_id,
                        error=str(e))
            return False
    
    async def verify_user_email(self, user_id: str, verification_token: str) -> bool:
        """Verify user email using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                # In a real implementation, you'd verify the verification_token
                # For now, just mark user as verified
                updated_user = await user_repo.update(user_id, {"is_verified": True})
                
                if updated_user:
                    logger.info("User email verified using repository pattern",
                               user_id=user_id)
                    return True
                
                return False
                
        except Exception as e:
            logger.error("Failed to verify email using repository pattern",
                        user_id=user_id,
                        error=str(e))
            return False
    
    async def get_user_statistics(self) -> Dict[str, Any]:
        """Get user statistics using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                # Get basic user statistics
                statistics = await user_repo.get_user_statistics()
                
                return statistics
                
        except Exception as e:
            logger.error("Failed to get user statistics using repository pattern", error=str(e))
            return {
                "total_users": 0,
                "active_users": 0,
                "verified_users": 0,
                "users_by_role": {},
                "recent_registrations_7d": 0
            }
    
    async def search_users(
        self,
        search_term: str,
        role: str = None,
        active_only: bool = True,
        skip: int = 0,
        limit: int = 50
    ) -> List[UserResponse]:
        """Search users by email or name using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                users = await user_repo.search_users(
                    search_term, role, active_only, skip, limit
                )
                
                return [
                    UserResponse(
                        id=str(user.id),
                        email=user.email,
                        full_name=user.full_name,
                        is_active=user.is_active,
                        is_verified=user.is_verified,
                        created_at=user.created_at,
                        role=user.role,
                        phone=getattr(user, 'phone', None),
                        language=getattr(user, 'language', None),
                        timezone=getattr(user, 'timezone', None)
                    )
                    for user in users
                ]
                
        except Exception as e:
            logger.error("Failed to search users using repository pattern",
                        search_term=search_term,
                        error=str(e))
            return []
    
    async def update_user_role(
        self,
        user_id: str,
        new_role: str,
        admin_user_id: str
    ) -> Optional[UserResponse]:
        """Update user role using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                
                # Validate role
                valid_roles = ["user", "admin", "manager", "super_admin"]
                if new_role not in valid_roles:
                    raise ValidationError(f"Invalid role. Must be one of: {valid_roles}")
                
                # Update user role
                updated_user = await user_repo.update(user_id, {"role": new_role})
                if not updated_user:
                    return None
                
                logger.info("User role updated using repository pattern",
                           user_id=user_id,
                           new_role=new_role,
                           admin_user_id=admin_user_id)
                
                return UserResponse(
                    id=str(updated_user.id),
                    email=updated_user.email,
                    full_name=updated_user.full_name,
                    is_active=updated_user.is_active,
                    is_verified=updated_user.is_verified,
                    created_at=updated_user.created_at,
                    role=updated_user.role,
                    phone=getattr(updated_user, 'phone', None),
                    language=getattr(updated_user, 'language', None),
                    timezone=getattr(updated_user, 'timezone', None)
                )
                
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Failed to update user role using repository pattern",
                        user_id=user_id,
                        new_role=new_role,
                        error=str(e))
            raise DatabaseError(f"Failed to update role: {str(e)}")

    async def update_user_field(
        self,
        user_id: str,
        field_name: str,
        field_value: Any
    ) -> bool:
        """Update a single field on a user record"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)

                # Update the specific field
                updated_user = await user_repo.update(user_id, {field_name: field_value})
                if not updated_user:
                    logger.error("User not found for field update",
                               user_id=user_id,
                               field_name=field_name)
                    return False

                await session.commit()

                logger.info("User field updated",
                           user_id=user_id,
                           field_name=field_name)

                return True

        except Exception as e:
            logger.error("Failed to update user field",
                        user_id=user_id,
                        field_name=field_name,
                        error=str(e))
            return False

    async def get_user_activity(self, user_id: str) -> Dict[str, Any]:
        """Get user activity information using repository pattern"""
        try:
            async with self.database_manager.get_session() as session:
                user_repo = UserRepository(User, session)
                token_repo = TokenRepository(RefreshToken, session)
                
                # Get user
                user = await user_repo.get_by_id(user_id)
                if not user:
                    return {"error": "User not found"}
                
                # Get token activity
                active_tokens = await token_repo.get_active_tokens_for_user(user_id)
                
                return {
                    "user_id": user_id,
                    "last_login": user.last_login.isoformat() if user.last_login else None,
                    "account_created": user.created_at.isoformat(),
                    "is_active": user.is_active,
                    "is_verified": user.is_verified,
                    "active_sessions": len(active_tokens),
                    "last_activity": max([token.created_at for token in active_tokens]).isoformat() if active_tokens else None
                }
                
        except Exception as e:
            logger.error("Failed to get user activity using repository pattern",
                        user_id=user_id,
                        error=str(e))
            return {"error": str(e)}

