"""
Password reset workflow - Feature 7

Secure password reset flow using time-limited tokens.
"""

from datetime import datetime, timedelta
from typing import Optional
from sqlalchemy.orm import Session

from .config import AuthConfig
from .models import User

# Import Rust crypto functions
try:
    from .irene_auth import generate_token, hash_token, verify_token_hash, hash_password
except ImportError:
    import irene_auth as _rust
    generate_token = _rust.generate_token
    hash_token = _rust.hash_token
    verify_token_hash = _rust.verify_token_hash
    hash_password = _rust.hash_password


class PasswordResetManager:
    """
    Password reset manager - Feature 7
    
    Implements secure password reset workflow:
    1. User requests reset
    2. Generate secure token (Rust CSPRNG)
    3. Hash and store token (Rust SHA-256)
    4. Send email with plaintext token
    5. User submits token + new password
    6. Verify token (Rust constant-time)
    7. Hash new password (Rust Argon2id)
    8. Update user, invalidate token
    """
    
    def __init__(self, config: AuthConfig):
        self.config = config
    
    def request_password_reset(self, email: str, db: Session) -> Optional[str]:
        """
        Initiate password reset for user
        
        Args:
            email: User's email address
            db: Database session
        
        Returns:
            Reset token (send to user via email) or None if user doesn't exist
        
        Security:
            - Returns None instead of error to prevent email enumeration
            - Token expires after configured time (default: 1 hour)
            - Old reset tokens automatically invalidated
        
        Example:
            >>> token = reset_manager.request_password_reset("user@example.com", db)
            >>> if token:
            ...     send_email(email, f"Reset: {token}")
        """
        user = db.query(User).filter_by(email=email).first()
        if not user:
            # Don't reveal if email exists (prevent enumeration)
            return None
        
        # Generate secure token (Rust)
        token = generate_token(32)
        
        # Hash for storage (Rust)
        token_hash = hash_token(token)
        
        # Set expiration
        expires = datetime.utcnow() + timedelta(hours=self.config.reset_token_expire_hours)
        
        # Store hash and expiration
        user.reset_token_hash = token_hash
        user.reset_token_expires = expires
        db.commit()
        
        return token
    
    def verify_reset_token(self, token: str, db: Session) -> Optional[User]:
        """
        Verify password reset token
        
        Args:
            token: Plaintext reset token
            db: Database session
        
        Returns:
            User object if token is valid, None otherwise
        
        Security:
            - Uses constant-time comparison (Rust)
            - Checks expiration
            - Doesn't reveal which part failed (token or expiration)
        """
        # Get users with active reset tokens
        users = db.query(User).filter(
            User.reset_token_hash.isnot(None),
            User.reset_token_expires.isnot(None)
        ).all()
        
        # Check each user's token with constant-time comparison (Rust)
        for user in users:
            if verify_token_hash(token, user.reset_token_hash):
                # Check expiration
                if user.reset_token_expires > datetime.utcnow():
                    return user
                else:
                    # Token expired - clean up
                    user.reset_token_hash = None
                    user.reset_token_expires = None
                    db.commit()
                    return None
        
        return None
    
    def reset_password(self, token: str, new_password: str, db: Session) -> bool:
        """
        Reset user's password with token
        
        Args:
            token: Valid reset token
            new_password: New plaintext password (will be hashed)
            db: Database session
        
        Returns:
            True if password was reset, False otherwise
        
        Raises:
            ValueError: If new password is invalid
        
        Example:
            >>> success = reset_manager.reset_password(token, "NewPass123!", db)
            >>> if success:
            ...     print("Password reset successful")
        """
        # Validate new password
        if len(new_password) < 8:
            raise ValueError("Password must be at least 8 characters")
        
        # Verify token
        user = self.verify_reset_token(token, db)
        if not user:
            return False
        
        # Hash new password (Rust Argon2id)
        user.password_hash = hash_password(new_password)
        
        # Invalidate reset token
        user.reset_token_hash = None
        user.reset_token_expires = None
        
        db.commit()
        return True
    
    def cancel_reset(self, email: str, db: Session) -> bool:
        """
        Cancel any pending password reset for user
        
        Args:
            email: User's email
            db: Database session
        
        Returns:
            True if reset was cancelled, False if no reset was pending
        """
        user = db.query(User).filter_by(email=email).first()
        if not user or not user.reset_token_hash:
            return False
        
        user.reset_token_hash = None
        user.reset_token_expires = None
        db.commit()
        return True
