"""
Biometric Authentication Module
Simplified: Use device fingerprint + biometric to unlock stored credentials
"""

import hashlib
import secrets
from datetime import datetime
from postgresql_models import SessionLocal, BiometricCredential
import logging

logger = logging.getLogger(__name__)

def register_biometric_credential(username, device_name, device_fingerprint, device_info):
    """Register a biometric-enabled device for a user"""
    from postgresql_models import User
    db = SessionLocal()
    try:
        # Check if this device is already registered for this user
        existing = db.query(BiometricCredential).filter_by(
            username=username,
            device_fingerprint=device_fingerprint
        ).first()
        
        if existing:
            # Update existing
            existing.device_name = device_name or existing.device_name
            existing.device_info = device_info or existing.device_info
            existing.enabled = 1
            existing.last_used = datetime.now()
            db.commit()
            logger.info(f"Updated biometric for {username} on device {device_name}")
            return {'success': True, 'message': 'Biometric updated'}
        
        # Find user in User table
        user = db.query(User).filter_by(username=username).first()
        user_id = user.id if user else None
        
        # Create new biometric registration
        credential = BiometricCredential(
            username=username,
            user_id=user_id,
            credential_id=device_fingerprint,  # Use device fingerprint as ID
            device_fingerprint=device_fingerprint,
            public_key='',  # Not used in this simplified approach
            device_name=device_name or 'Unknown Device',
            device_info=device_info or '',
            enabled=1,
            created_at=datetime.now(),
            last_used=datetime.now()
        )
        
        db.add(credential)
        db.commit()
        
        logger.info(f"Registered biometric for {username} on device {device_name}")
        
        return {
            'success': True,
            'message': 'Biometric registered successfully'
        }
    except Exception as e:
        db.rollback()
        logger.error(f"Error registering biometric: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def authenticate_biometric_by_device(username, device_fingerprint):
    """
    Authenticate user by verifying device fingerprint
    The browser already verified the biometric, we just check if this device is registered
    If username is None, lookup by device fingerprint alone
    """
    from postgresql_models import User
    db = SessionLocal()
    try:
        logger.info(f"Biometric auth for {username or 'unknown'} from device {device_fingerprint}")
        
        # Find biometric registration - either by username+fingerprint or just fingerprint
        if username:
            credential = db.query(BiometricCredential).filter_by(
                username=username,
                device_fingerprint=device_fingerprint,
                enabled=1
            ).first()
        else:
            # No username provided - lookup by device fingerprint alone
            credential = db.query(BiometricCredential).filter_by(
                device_fingerprint=device_fingerprint,
                enabled=1
            ).first()
        
        if not credential:
            logger.warning(f"No biometric found for device {device_fingerprint}")
            return {'success': False, 'error': 'No biometric setup found for this device'}
        
        # Get username from credential if not provided
        if not username:
            username = credential.username
            logger.info(f"Found username {username} from device fingerprint")
        
        # Get user details
        user = db.query(User).filter_by(username=username).first()
        
        if not user:
            logger.error(f"User {username} not found in database")
            return {'success': False, 'error': 'User not found'}
        
        # Update last used
        credential.last_used = datetime.now()
        db.commit()
        
        logger.info(f"Biometric auth successful for {username}")
        
        return {
            'success': True,
            'username': user.username,
            'role': user.role,
            'permissions': user.get_permissions_list()
        }
    except Exception as e:
        logger.error(f"Error in biometric authentication: {e}", exc_info=True)
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def get_user_biometric_status(username):
    """Check if user has biometric enabled"""
    db = SessionLocal()
    try:
        credentials = db.query(BiometricCredential).filter_by(
            username=username,
            enabled=1
        ).all()
        
        return {
            'success': True,
            'has_biometric': len(credentials) > 0,
            'device_count': len(credentials),
            'devices': [{
                'device_name': c.device_name,
                'last_used': c.last_used.isoformat() if c.last_used else None
            } for c in credentials]
        }
    except Exception as e:
        logger.error(f"Error checking biometric status: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()
    """Register a new biometric credential for a user"""
    from postgresql_models import User  # Import here to avoid circular import
    db = SessionLocal()
    try:
        # Check if credential already exists
        existing = db.query(BiometricCredential).filter_by(credential_id=credential_id).first()
        if existing:
            return {'success': False, 'error': 'credential_exists'}
        
        # Find user in User table
        user = db.query(User).filter_by(username=username).first()
        user_id = user.id if user else None
        
        # Create new credential
        credential = BiometricCredential(
            username=username,
            user_id=user_id,  # Link to User table if exists
            credential_id=credential_id,
            public_key=public_key,
            device_name=device_name or 'Unknown Device',
            device_info=device_info or '',
            enabled=1,
            created_at=datetime.now(),
            last_used=datetime.now()
        )
        
        db.add(credential)
        db.commit()
        db.refresh(credential)
        
        return {
            'success': True,
            'credential': {
                'id': credential.id,
                'device_name': credential.device_name,
                'created_at': credential.created_at.isoformat()
            }
        }
    except Exception as e:
        db.rollback()
        logger.error(f"Error registering biometric credential: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def authenticate_biometric(username, credential_id, signature, challenge):
    """Authenticate using biometric credential"""
    db = SessionLocal()
    try:
        logger.info(f"Authenticating biometric for user: {username}")
        logger.info(f"Credential ID (first 40 chars): {credential_id[:40] if credential_id else 'None'}...")
        logger.info(f"Credential ID length: {len(credential_id) if credential_id else 0}")
        
        # First, check if user has any credentials at all
        all_credentials = db.query(BiometricCredential).filter_by(username=username).all()
        logger.info(f"User {username} has {len(all_credentials)} total credentials in database")
        
        if all_credentials:
            for idx, cred in enumerate(all_credentials):
                logger.info(f"  Credential {idx+1}: ID (first 40): {cred.credential_id[:40]}..., enabled: {cred.enabled}, device: {cred.device_name}")
        
        # Find credential - match by username first, then credential_id
        credential = db.query(BiometricCredential).filter_by(
            username=username,
            credential_id=credential_id,
            enabled=1
        ).first()
        
        if not credential:
            logger.warning(f"No matching enabled credential found for {username}")
            # Try to find any enabled credential for debugging
            any_enabled = db.query(BiometricCredential).filter_by(username=username, enabled=1).first()
            if any_enabled:
                logger.warning(f"User has enabled credential but ID doesn't match.")
                logger.warning(f"  Expected (first 40): {any_enabled.credential_id[:40]}...")
                logger.warning(f"  Received (first 40): {credential_id[:40]}...")
            else:
                logger.warning(f"User has no enabled credentials at all")
            return {'success': False, 'error': 'credential_not_found'}
        
        logger.info(f"Credential found for {username} on device: {credential.device_name}")
        
        # Verify signature (currently simplified - accepts all)
        if not verify_signature(credential.public_key, signature, challenge):
            logger.error(f"Signature verification failed for {username}")
            return {'success': False, 'error': 'invalid_signature'}
        
        # Update last used timestamp
        credential.last_used = datetime.now()
        db.commit()
        
        logger.info(f"Biometric authentication successful for {username}")
        
        return {
            'success': True,
            'username': username,
            'device_name': credential.device_name
        }
    except Exception as e:
        logger.error(f"Error authenticating biometric: {e}", exc_info=True)
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def authenticate_biometric_by_credential(credential_id, signature, challenge):
    """
    Authenticate using biometric credential - looks up username from credential ID
    This is the proper WebAuthn flow where the credential identifies the user
    """
    db = SessionLocal()
    try:
        logger.info(f"Authenticating by credential ID (first 40): {credential_id[:40]}...")
        logger.info(f"Credential ID length: {len(credential_id)}")
        
        # Find credential by ID only (no username needed)
        credential = db.query(BiometricCredential).filter_by(
            credential_id=credential_id,
            enabled=1
        ).first()
        
        if not credential:
            logger.warning(f"No enabled credential found with this ID")
            # Debug: show all credentials
            all_creds = db.query(BiometricCredential).filter_by(enabled=1).all()
            logger.info(f"Total enabled credentials in database: {len(all_creds)}")
            for idx, cred in enumerate(all_creds[:5]):  # Show first 5
                logger.info(f"  Cred {idx+1}: User={cred.username}, ID (first 40)={cred.credential_id[:40]}...")
            return {'success': False, 'error': 'credential_not_found'}
        
        username = credential.username
        logger.info(f"Credential found! Belongs to user: {username}, device: {credential.device_name}")
        
        # Verify signature
        if not verify_signature(credential.public_key, signature, challenge):
            logger.error(f"Signature verification failed for credential")
            return {'success': False, 'error': 'invalid_signature'}
        
        # Update last used timestamp
        credential.last_used = datetime.now()
        db.commit()
        
        logger.info(f"Biometric authentication successful for {username}")
        
        return {
            'success': True,
            'username': username,
            'device_name': credential.device_name
        }
    except Exception as e:
        logger.error(f"Error authenticating biometric by credential: {e}", exc_info=True)
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def get_user_credentials(username):
    """Get all biometric credentials for a user"""
    db = SessionLocal()
    try:
        credentials = db.query(BiometricCredential).filter_by(username=username).all()
        return {
            'success': True,
            'credentials': [{
                'id': c.id,
                'device_name': c.device_name,
                'device_info': c.device_info,
                'enabled': bool(c.enabled),
                'created_at': c.created_at.isoformat(),
                'last_used': c.last_used.isoformat() if c.last_used else None
            } for c in credentials]
        }
    except Exception as e:
        logger.error(f"Error fetching credentials: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def toggle_credential(credential_id, enabled):
    """Enable or disable a biometric credential"""
    db = SessionLocal()
    try:
        credential = db.query(BiometricCredential).filter_by(id=credential_id).first()
        if not credential:
            return {'success': False, 'error': 'credential_not_found'}
        
        credential.enabled = 1 if enabled else 0
        db.commit()
        
        return {'success': True, 'enabled': bool(credential.enabled)}
    except Exception as e:
        db.rollback()
        logger.error(f"Error toggling credential: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def delete_credential(credential_id):
    """Delete a biometric credential"""
    db = SessionLocal()
    try:
        credential = db.query(BiometricCredential).filter_by(id=credential_id).first()
        if not credential:
            return {'success': False, 'error': 'credential_not_found'}
        
        db.delete(credential)
        db.commit()
        
        return {'success': True}
    except Exception as e:
        db.rollback()
        logger.error(f"Error deleting credential: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()

def delete_all_user_biometrics(username):
    """Delete all biometric credentials for a user"""
    db = SessionLocal()
    try:
        # Delete all credentials for this user
        deleted_count = db.query(BiometricCredential).filter_by(username=username).delete()
        db.commit()
        
        logger.info(f"Deleted {deleted_count} biometric credential(s) for user {username}")
        return {'success': True, 'deleted_count': deleted_count}
    except Exception as e:
        db.rollback()
        logger.error(f"Error deleting biometrics for {username}: {e}")
        return {'success': False, 'error': str(e)}
    finally:
        db.close()
