"""
Core Models for Stream Processor Application

This module contains abstract base models and shared functionality
that is used across multiple applications in the Stream Processor project.
These models provide common fields and methods that promote consistency
and reduce code duplication.
"""

import uuid
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from datetime import timedelta

from apps.core.models import TimestampedModel



class UserProfile(TimestampedModel):
    """
    Extended user profile with security features.
    
    This model extends the Django User model with additional security
    and authentication features including 2FA, session tracking,
    and account security settings.
    """
    
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        related_name='security_profile'
    )
    
    # Two-Factor Authentication
    totp_secret = models.CharField(
        max_length=32,
        blank=True,
        help_text="TOTP secret key for 2FA"
    )
    
    is_2fa_enabled = models.BooleanField(
        default=False,
        help_text="Whether 2FA is enabled for this user"
    )
    
    backup_codes = models.JSONField(
        default=list,
        help_text="Backup codes for 2FA recovery"
    )
    
    # Account Security
    failed_login_attempts = models.PositiveIntegerField(
        default=0,
        help_text="Number of consecutive failed login attempts"
    )
    
    account_locked_until = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Account lockout expiration time"
    )
    
    last_login_ip = models.GenericIPAddressField(
        null=True,
        blank=True,
        help_text="IP address of last successful login"
    )
    
    last_password_change = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Timestamp of last password change"
    )
    
    password_reset_required = models.BooleanField(
        default=False,
        help_text="Whether user must reset password on next login"
    )
    
    # Session Management
    max_concurrent_sessions = models.PositiveIntegerField(
        default=3,
        help_text="Maximum number of concurrent sessions allowed"
    )
    
    session_timeout_minutes = models.PositiveIntegerField(
        default=480,  # 8 hours
        help_text="Session timeout in minutes"
    )
    
    class Meta:
        verbose_name = "User Security Profile"
        verbose_name_plural = "User Security Profiles"
    
    def __str__(self):
        return f"Security Profile for {self.user.username}"
    
    def is_account_locked(self) -> bool:
        """
        Check if the account is currently locked.
        
        Returns:
            bool: True if account is locked, False otherwise
        """
        if not self.account_locked_until:
            return False
        return timezone.now() < self.account_locked_until
    
    def lock_account(self, duration_minutes: int = 30) -> None:
        """
        Lock the account for a specified duration.
        
        Args:
            duration_minutes: How long to lock the account
        """
        self.account_locked_until = timezone.now() + timedelta(minutes=duration_minutes)
        self.save(update_fields=['account_locked_until'])
    
    def unlock_account(self) -> None:
        """
        Unlock the account and reset failed login attempts.
        """
        self.account_locked_until = None
        self.failed_login_attempts = 0
        self.save(update_fields=['account_locked_until', 'failed_login_attempts'])
    
    def increment_failed_attempts(self) -> None:
        """
        Increment failed login attempts and lock account if threshold reached.
        """
        self.failed_login_attempts += 1
        if self.failed_login_attempts >= 5:
            self.lock_account(30)  # Lock for 30 minutes
        self.save(update_fields=['failed_login_attempts'])
    
    def reset_failed_attempts(self) -> None:
        """
        Reset failed login attempts counter.
        """
        self.failed_login_attempts = 0
        self.save(update_fields=['failed_login_attempts'])
    
    def needs_password_change(self) -> bool:
        """
        Check if the user needs to change their password.
        
        Returns:
            bool: True if password change is required, False otherwise
        """
        return self.password_reset_required


class LoginAttempt(TimestampedModel):
    """
    Track login attempts for security monitoring.
    """
    
    username = models.CharField(max_length=150)
    ip_address = models.GenericIPAddressField()
    user_agent = models.TextField(blank=True)
    success = models.BooleanField()
    failure_reason = models.CharField(max_length=100, blank=True)
    
    class Meta:
        verbose_name = "Login Attempt"
        verbose_name_plural = "Login Attempts"
        ordering = ['-created_at']
    
    def __str__(self):
        status = "Success" if self.success else "Failed"
        return f"{self.username} - {status} - {self.ip_address}"


class UserSession(TimestampedModel):
    """
    Track active user sessions.
    """
    
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    session_key = models.CharField(max_length=40, unique=True)
    ip_address = models.GenericIPAddressField()
    user_agent = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)
    last_activity = models.DateTimeField(auto_now=True)
    
    class Meta:
        verbose_name = "User Session"
        verbose_name_plural = "User Sessions"
        ordering = ['-last_activity']
    
    def __str__(self):
        return f"{self.user.username} - {self.session_key[:8]}..."
