"""User service for managing user operations."""

from typing import Optional, List, Tuple
from sqlalchemy.orm import Session
from sqlalchemy import or_, and_, desc

from ..models.user import User
from ..utils.schemas import UserCreate, UserUpdate
from ..utils.exceptions import DatabaseError, ValidationError
from ..utils.logger import get_logger
from .auth import AuthService

logger = get_logger(__name__)


class UserService:
    """Service for user management operations."""
    
    def __init__(self, db: Session):
        self.db = db
    
    def get_password_hash(self, password: str) -> str:
        """Hash a password."""
        return AuthService.get_password_hash(password)
    
    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """Verify a password against its hash."""
        return AuthService.verify_password(plain_password, hashed_password)
    
    def get_user_by_id(self, user_id: int) -> Optional[User]:
        """Get user by ID."""
        try:
            # Use options to avoid loading problematic relationships
            from sqlalchemy.orm import noload
            return self.db.query(User).options(
                noload(User.roles)
            ).filter(User.id == user_id).first()
        except Exception as e:
            logger.error(f"Error getting user by ID {user_id}: {e}")
            raise DatabaseError(f"Failed to get user: {str(e)}")
    
    def get_user(self, user_id: int) -> Optional[User]:
        """Get user by ID (alias for get_user_by_id)."""
        return self.get_user_by_id(user_id)
    
    def get_user_by_email(self, email: str) -> Optional[User]:
        """Get user by email."""
        try:
            return self.db.query(User).filter(User.email == email).first()
        except Exception as e:
            logger.error(f"Error getting user by email {email}: {e}")
            raise DatabaseError(f"Failed to get user: {str(e)}")
    
    def get_user_by_username(self, username: str) -> Optional[User]:
        """Get user by username."""
        try:
            return self.db.query(User).filter(User.username == username).first()
        except Exception as e:
            logger.error(f"Error getting user by username {username}: {e}")
            raise DatabaseError(f"Failed to get user: {str(e)}")
    
    def create_user(self, user_data: UserCreate) -> User:
        """Create a new user."""
        try:
            # Validate input
            if len(user_data.password) < 6:
                raise ValidationError("Password must be at least 6 characters long")
            if len(user_data.password) > 72:
                raise ValidationError("Password cannot be longer than 72 characters")
            
            # Hash password
            hashed_password = self.get_password_hash(user_data.password)
            
            # Create user
            db_user = User(
                username=user_data.username,
                email=user_data.email,
                hashed_password=hashed_password,
                full_name=user_data.full_name,
                is_active=True
            )
            
            # Set audit fields
            db_user.set_audit_fields()
            
            self.db.add(db_user)
            self.db.commit()
            self.db.refresh(db_user)
            
            logger.info(f"User created successfully: {user_data.username}")
            return db_user
            
        except ValidationError:
            raise
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error creating user {user_data.username}: {e}")
            raise DatabaseError(f"Failed to create user: {str(e)}")
    
    def update_user(self, user_id: int, user_update: UserUpdate) -> Optional[User]:
        """Update user information."""
        try:
            user = self.get_user_by_id(user_id)
            if not user:
                return None
            
            # Update fields
            update_data = user_update.dict(exclude_unset=True)
            
            if "password" in update_data:
                update_data["hashed_password"] = self.get_password_hash(update_data.pop("password"))
            
            for field, value in update_data.items():
                setattr(user, field, value)
            
            # Skip audit fields for now due to database schema mismatch
            # user.set_audit_fields(is_update=True)
            
            self.db.commit()
            self.db.refresh(user)
            
            logger.info(f"User updated successfully: {user.username}")
            return user
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error updating user {user_id}: {e}")
            raise DatabaseError(f"Failed to update user: {str(e)}")
    
    def get_users(self, skip: int = 0, limit: int = 100) -> List[User]:
        """Get all users with pagination."""
        try:
            return self.db.query(User).offset(skip).limit(limit).all()
        except Exception as e:
            logger.error(f"Error getting users: {e}")
            raise DatabaseError(f"Failed to get users: {str(e)}")
    
    def get_users_with_filters(
        self, 
        skip: int = 0, 
        limit: int = 100,
        search: Optional[str] = None,
        role_id: Optional[int] = None,
        is_active: Optional[bool] = None
    ) -> Tuple[List[User], int]:
        """Get users with filters and return total count."""
        try:
            query = self.db.query(User).order_by(desc('created_at'))
            
            # Apply filters
            if search:
                search_term = f"%{search}%"
                query = query.filter(
                    or_(
                        User.username.ilike(search_term),
                        User.email.ilike(search_term),
                        User.full_name.ilike(search_term)
                    )
                )
            
            if role_id is not None:
                from ..models.permission import UserRole
                query = query.join(UserRole).filter(UserRole.role_id == role_id)
            
            if is_active is not None:
                query = query.filter(User.is_active == is_active)
            
            # Get total count
            total = query.count()
            
            # Apply pagination
            users = query.offset(skip).limit(limit).all()
            
            return users, total
            
        except Exception as e:
            logger.error(f"Error getting users with filters: {e}")
            raise DatabaseError(f"Failed to get users: {str(e)}")
    
    def delete_user(self, user_id: int) -> bool:
        """Delete a user."""
        try:
            user = self.get_user_by_id(user_id)
            if not user:
                return False
            
            # Manually delete related records to avoid cascade issues
            from sqlalchemy import text
            
            # Delete user_roles records
            self.db.execute(text("DELETE FROM user_roles WHERE user_id = :user_id"), {"user_id": user_id})
            
            # Now delete the user
            self.db.delete(user)
            self.db.commit()
            
            logger.info(f"User deleted successfully: {user.username}")
            return True
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error deleting user {user_id}: {e}")
            raise DatabaseError(f"Failed to delete user: {str(e)}")
    
    def authenticate_user(self, username: str, password: str) -> Optional[User]:
        """Authenticate user with username and password."""
        try:
            user = self.get_user_by_username(username)
            if not user:
                return None
            
            if not self.verify_password(password, user.hashed_password):
                return None
            
            if not user.is_active:
                return None
            
            return user
            
        except Exception as e:
            logger.error(f"Error authenticating user {username}: {e}")
            return None
    
    def change_password(self, user_id: int, current_password: str, new_password: str) -> bool:
        """Change user password."""
        try:
            user = self.get_user_by_id(user_id)
            if not user:
                raise ValidationError("User not found")
            
            # Verify current password
            if not self.verify_password(current_password, user.hashed_password):
                raise ValidationError("Current password is incorrect")
            
            # Validate new password
            if len(new_password) < 6:
                raise ValidationError("New password must be at least 6 characters long")
            
            # Hash new password
            hashed_password = self.get_password_hash(new_password)
            
            # Update password
            user.hashed_password = hashed_password
            self.db.commit()
            
            logger.info(f"Password changed successfully for user: {user.username}")
            return True
            
        except ValidationError:
            raise
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error changing password for user {user_id}: {e}")
            raise DatabaseError(f"Failed to change password: {str(e)}")
    
    def reset_password(self, user_id: int, new_password: str) -> bool:
        """Reset user password (admin only, no current password required)."""
        try:
            user = self.get_user_by_id(user_id)
            if not user:
                raise ValidationError("User not found")
            
            # Validate new password
            if len(new_password) < 6:
                raise ValidationError("New password must be at least 6 characters long")
            
            # Hash new password
            hashed_password = self.get_password_hash(new_password)
            
            # Update password
            user.hashed_password = hashed_password
            self.db.commit()
            
            logger.info(f"Password reset successfully for user: {user.username}")
            return True
            
        except ValidationError:
            raise
        except Exception as e:
            self.db.rollback()
            logger.error(f"Error resetting password for user {user_id}: {e}")
            raise DatabaseError(f"Failed to reset password: {str(e)}")