"""
NovaLang Security Service - Built-in authentication and security features
Includes password hashing (bcrypt) and JWT token generation/verification
"""

import bcrypt
import jwt
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
import os

class SecurityService:
    """Built-in security service for NovaLang applications"""
    
    def __init__(self, secret_key: Optional[str] = None):
        """
        Initialize security service
        
        Args:
            secret_key: Secret key for JWT signing (from env or random)
        """
        self.secret_key = secret_key or os.getenv('SECRET_KEY', self._generate_secret_key())
        self.algorithm = 'HS256'
        self.token_expiry_hours = int(os.getenv('JWT_EXPIRY_HOURS', '24'))
    
    def _generate_secret_key(self) -> str:
        """Generate a random secret key"""
        import secrets
        return secrets.token_urlsafe(32)
    
    # ============= Password Hashing (bcrypt) =============
    
    def hash_password(self, password: str) -> str:
        """
        Hash a password using bcrypt
        
        Args:
            password: Plain text password
        
        Returns:
            str: Hashed password (safe to store in database)
        
        Example:
            hashed = security_service.hash_password("mypassword123")
            # Store hashed in database
        """
        # Generate salt and hash password
        salt = bcrypt.gensalt()
        hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
        return hashed.decode('utf-8')
    
    def verify_password(self, password: str, hashed_password: str) -> bool:
        """
        Verify a password against its hash
        
        Args:
            password: Plain text password to verify
            hashed_password: Hashed password from database
        
        Returns:
            bool: True if password matches, False otherwise
        
        Example:
            if security_service.verify_password(user_input, user.password):
                # Password correct - log in user
            else:
                # Password incorrect
        """
        try:
            return bcrypt.checkpw(
                password.encode('utf-8'),
                hashed_password.encode('utf-8')
            )
        except Exception:
            return False
    
    # ============= JWT Tokens =============
    
    def generate_token(self, user_id: Any, username: str, 
                      additional_claims: Optional[Dict[str, Any]] = None,
                      expiry_hours: Optional[int] = None) -> str:
        """
        Generate a JWT token for authentication
        
        Args:
            user_id: User's unique identifier
            username: Username
            additional_claims: Optional extra data to include in token
            expiry_hours: Token expiry in hours (default: 24)
        
        Returns:
            str: JWT token string
        
        Example:
            token = security_service.generate_token(
                user_id=user.id,
                username=user.username,
                additional_claims={'role': 'admin'}
            )
            # Return token to client
        """
        expiry = expiry_hours or self.token_expiry_hours
        
        payload = {
            'user_id': user_id,
            'username': username,
            'exp': datetime.utcnow() + timedelta(hours=expiry),
            'iat': datetime.utcnow()
        }
        
        # Add any additional claims
        if additional_claims:
            payload.update(additional_claims)
        
        token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
        return token
    
    def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
        """
        Verify and decode a JWT token
        
        Args:
            token: JWT token string
        
        Returns:
            dict: Token payload if valid, None if invalid/expired
        
        Example:
            payload = security_service.verify_token(token)
            if payload:
                user_id = payload['user_id']
                username = payload['username']
                # Token valid - proceed
            else:
                # Token invalid or expired
        """
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            return payload
        except jwt.ExpiredSignatureError:
            print("⚠️ Token expired")
            return None
        except jwt.InvalidTokenError:
            print("⚠️ Invalid token")
            return None
    
    def generate_refresh_token(self, user_id: Any, username: str) -> str:
        """
        Generate a long-lived refresh token (7 days)
        
        Args:
            user_id: User's unique identifier
            username: Username
        
        Returns:
            str: Refresh token
        
        Example:
            refresh_token = security_service.generate_refresh_token(
                user_id=user.id,
                username=user.username
            )
        """
        return self.generate_token(
            user_id=user_id,
            username=username,
            additional_claims={'type': 'refresh'},
            expiry_hours=168  # 7 days
        )
    
    # ============= Password Reset Tokens =============
    
    def generate_password_reset_token(self, user_id: Any, email: str) -> str:
        """
        Generate a short-lived password reset token (1 hour)
        
        Args:
            user_id: User's unique identifier
            email: User's email address
        
        Returns:
            str: Password reset token
        
        Example:
            reset_token = security_service.generate_password_reset_token(
                user_id=user.id,
                email=user.email
            )
            # Send token via email
        """
        payload = {
            'user_id': user_id,
            'email': email,
            'type': 'password_reset',
            'exp': datetime.utcnow() + timedelta(hours=1),
            'iat': datetime.utcnow()
        }
        
        token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
        return token
    
    def verify_password_reset_token(self, token: str) -> Optional[Dict[str, Any]]:
        """
        Verify a password reset token
        
        Args:
            token: Password reset token
        
        Returns:
            dict: Token payload if valid, None if invalid/expired
        """
        payload = self.verify_token(token)
        if payload and payload.get('type') == 'password_reset':
            return payload
        return None
    
    # ============= Email Verification Tokens =============
    
    def generate_email_verification_token(self, user_id: Any, email: str) -> str:
        """
        Generate an email verification token (48 hours)
        
        Args:
            user_id: User's unique identifier
            email: Email address to verify
        
        Returns:
            str: Email verification token
        """
        payload = {
            'user_id': user_id,
            'email': email,
            'type': 'email_verification',
            'exp': datetime.utcnow() + timedelta(hours=48),
            'iat': datetime.utcnow()
        }
        
        token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
        return token
    
    def verify_email_verification_token(self, token: str) -> Optional[Dict[str, Any]]:
        """
        Verify an email verification token
        
        Args:
            token: Email verification token
        
        Returns:
            dict: Token payload if valid, None if invalid/expired
        """
        payload = self.verify_token(token)
        if payload and payload.get('type') == 'email_verification':
            return payload
        return None


# Singleton instance
_security_service = None

def get_security_service() -> SecurityService:
    """Get the global security service instance"""
    global _security_service
    if _security_service is None:
        _security_service = SecurityService()
    return _security_service

def init_security_service(secret_key: Optional[str] = None) -> SecurityService:
    """Initialize the global security service"""
    global _security_service
    _security_service = SecurityService(secret_key)
    return _security_service
