"""
User Repository
Repository for user operations with authentication-specific queries
"""

from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, func, desc, text
from datetime import datetime, timezone, timedelta
import structlog

from .base import AuthBaseRepository
from app.models.users import User
from shared.database.exceptions import DatabaseError, ValidationError, DuplicateRecordError

logger = structlog.get_logger()


class UserRepository(AuthBaseRepository):
    """Repository for user operations"""
    
    def __init__(self, model, session: AsyncSession, cache_ttl: Optional[int] = 600):
        super().__init__(model, session, cache_ttl)
    
    async def create_user(self, user_data: Dict[str, Any]) -> User:
        """Create a new user with validation"""
        try:
            # Validate user data
            validation_result = self._validate_auth_data(
                user_data, 
                ["email", "hashed_password", "full_name", "role"]
            )
            
            if not validation_result["is_valid"]:
                raise ValidationError(f"Invalid user data: {validation_result['errors']}")
            
            # Check if user already exists
            existing_user = await self.get_by_email(user_data["email"])
            if existing_user:
                raise DuplicateRecordError(f"User with email {user_data['email']} already exists")
            
            # Create user
            user = await self.create(user_data)
            
            logger.info("User created successfully", 
                       user_id=user.id,
                       email=user.email,
                       role=user.role)
            
            return user
            
        except (ValidationError, DuplicateRecordError):
            raise
        except Exception as e:
            logger.error("Failed to create user", 
                        email=user_data.get("email"),
                        error=str(e))
            raise DatabaseError(f"Failed to create user: {str(e)}")
    
    async def get_user_by_email(self, email: str) -> Optional[User]:
        """Get user by email address"""
        return await self.get_by_email(email)
    
    async def get_active_users(self, skip: int = 0, limit: int = 100) -> List[User]:
        """Get all active users"""
        return await self.get_active_records(skip=skip, limit=limit)
    
    async def authenticate_user(self, email: str, password: str) -> Optional[User]:
        """Authenticate user with email and plain password"""
        try:
            user = await self.get_by_email(email)
            
            if not user:
                logger.debug("User not found for authentication", email=email)
                return None
            
            if not user.is_active:
                logger.debug("User account is inactive", email=email)
                return None
            
            # Verify password using security manager
            from app.core.security import SecurityManager
            if SecurityManager.verify_password(password, user.hashed_password):
                # Update last login
                await self.update_last_login(user.id)
                logger.info("User authenticated successfully", 
                           user_id=user.id, 
                           email=email)
                return user
            
            logger.debug("Invalid password for user", email=email)
            return None
            
        except Exception as e:
            logger.error("Authentication failed", 
                        email=email,
                        error=str(e))
            raise DatabaseError(f"Authentication failed: {str(e)}")
    
    async def update_last_login(self, user_id: str) -> Optional[User]:
        """Update user's last login timestamp"""
        try:
            return await self.update(user_id, {
                "last_login": datetime.now(timezone.utc)
            })
        except Exception as e:
            logger.error("Failed to update last login", 
                        user_id=user_id,
                        error=str(e))
            # Don't raise here - last login update is not critical
            return None
    
    async def update_user_profile(self, user_id: str, profile_data: Dict[str, Any]) -> Optional[User]:
        """Update user profile information"""
        try:
            # Remove sensitive fields that shouldn't be updated via profile
            profile_data.pop("id", None)
            profile_data.pop("hashed_password", None)
            profile_data.pop("created_at", None)
            profile_data.pop("is_active", None)
            
            # Validate email if being updated
            if "email" in profile_data:
                validation_result = self._validate_auth_data(
                    profile_data, 
                    ["email"]
                )
                if not validation_result["is_valid"]:
                    raise ValidationError(f"Invalid profile data: {validation_result['errors']}")
                
                # Check for email conflicts
                existing_user = await self.get_by_email(profile_data["email"])
                if existing_user and str(existing_user.id) != str(user_id):
                    raise DuplicateRecordError(f"Email {profile_data['email']} is already in use")
            
            updated_user = await self.update(user_id, profile_data)
            
            if updated_user:
                logger.info("User profile updated", 
                           user_id=user_id,
                           updated_fields=list(profile_data.keys()))
            
            return updated_user
            
        except (ValidationError, DuplicateRecordError):
            raise
        except Exception as e:
            logger.error("Failed to update user profile", 
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to update profile: {str(e)}")
    
    async def change_password(self, user_id: str, new_password_hash: str) -> bool:
        """Change user password"""
        try:
            updated_user = await self.update(user_id, {
                "hashed_password": new_password_hash
            })
            
            if updated_user:
                logger.info("Password changed successfully", user_id=user_id)
                return True
            
            return False
            
        except Exception as e:
            logger.error("Failed to change password", 
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to change password: {str(e)}")
    
    async def verify_user_email(self, user_id: str) -> Optional[User]:
        """Mark user email as verified"""
        try:
            return await self.update(user_id, {
                "is_verified": True
            })
        except Exception as e:
            logger.error("Failed to verify user email", 
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to verify email: {str(e)}")
    
    async def deactivate_user(self, user_id: str) -> Optional[User]:
        """Deactivate user account"""
        return await self.deactivate_record(user_id)
    
    async def activate_user(self, user_id: str) -> Optional[User]:
        """Activate user account"""
        return await self.activate_record(user_id)
    
    async def get_users_by_role(self, role: str, skip: int = 0, limit: int = 100) -> List[User]:
        """Get users by role"""
        try:
            return await self.get_multi(
                skip=skip,
                limit=limit,
                filters={"role": role, "is_active": True},
                order_by="created_at",
                order_desc=True
            )
        except Exception as e:
            logger.error("Failed to get users by role", 
                        role=role,
                        error=str(e))
            raise DatabaseError(f"Failed to get users by role: {str(e)}")
    
    async def search_users(self, search_term: str, skip: int = 0, limit: int = 50) -> List[User]:
        """Search users by email or full name"""
        try:
            return await self.search(
                search_term=search_term,
                search_fields=["email", "full_name"],
                skip=skip,
                limit=limit
            )
        except Exception as e:
            logger.error("Failed to search users", 
                        search_term=search_term,
                        error=str(e))
            raise DatabaseError(f"Failed to search users: {str(e)}")
    
    async def get_user_statistics(self) -> Dict[str, Any]:
        """Get user statistics"""
        try:
            # Get basic counts
            total_users = await self.count()
            active_users = await self.count(filters={"is_active": True})
            verified_users = await self.count(filters={"is_verified": True})
            
            # Get users by role using raw query
            role_query = text("""
                SELECT role, COUNT(*) as count 
                FROM users 
                WHERE is_active = true
                GROUP BY role
                ORDER BY count DESC
            """)
            
            result = await self.session.execute(role_query)
            role_stats = {row.role: row.count for row in result.fetchall()}
            
            # Recent activity (users created in last 30 days)
            thirty_days_ago = datetime.now(timezone.utc) - timedelta(days=30)
            recent_users_query = text("""
                SELECT COUNT(*) as count 
                FROM users 
                WHERE created_at >= :thirty_days_ago
            """)
            
            recent_result = await self.session.execute(
                recent_users_query, 
                {"thirty_days_ago": thirty_days_ago}
            )
            recent_users = recent_result.scalar() or 0
            
            return {
                "total_users": total_users,
                "active_users": active_users,
                "inactive_users": total_users - active_users,
                "verified_users": verified_users,
                "unverified_users": active_users - verified_users,
                "recent_registrations": recent_users,
                "users_by_role": role_stats
            }
            
        except Exception as e:
            logger.error("Failed to get user statistics", error=str(e))
            return {
                "total_users": 0,
                "active_users": 0,
                "inactive_users": 0,
                "verified_users": 0,
                "unverified_users": 0,
                "recent_registrations": 0,
                "users_by_role": {}
            }