"""
Enhanced audit trail models for comprehensive logging and security monitoring
"""
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
import json

User = get_user_model()


class AuditEventType(models.TextChoices):
    """Types of audit events"""
    LOGIN = 'login', 'Login'
    LOGOUT = 'logout', 'Logout'
    PERMISSION_CHANGE = 'permission_change', 'Permission Change'
    DATA_ACCESS = 'data_access', 'Data Access'
    DATA_MODIFICATION = 'data_modification', 'Data Modification'
    SECURITY_VIOLATION = 'security_violation', 'Security Violation'
    SYSTEM_CONFIGURATION = 'system_config', 'System Configuration'
    EXPORT_DATA = 'export_data', 'Data Export'
    REPORT_GENERATION = 'report_generation', 'Report Generation'
    FAILED_ACCESS = 'failed_access', 'Failed Access Attempt'
    BULK_OPERATION = 'bulk_operation', 'Bulk Operation'


class SecurityEventSeverity(models.TextChoices):
    """Security event severity levels"""
    LOW = 'low', 'Low'
    MEDIUM = 'medium', 'Medium'
    HIGH = 'high', 'High'
    CRITICAL = 'critical', 'Critical'


class EnhancedAuditLog(models.Model):
    """
    Enhanced audit log with detailed permission and security tracking
    """
    # Basic event information
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='enhanced_audit_logs', null=True, blank=True)
    event_type = models.CharField(max_length=50, choices=AuditEventType.choices)
    action = models.CharField(max_length=100)
    module = models.CharField(max_length=50)
    
    # Permission-specific fields
    permission_checked = models.CharField(max_length=100, blank=True, null=True)
    permission_granted = models.BooleanField(null=True, blank=True)
    role_at_time = models.CharField(max_length=50, blank=True, null=True)
    branch_context = models.CharField(max_length=100, blank=True, null=True)
    
    # Object tracking (generic foreign key for any model)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
    object_id = models.PositiveIntegerField(null=True, blank=True)
    content_object = GenericForeignKey('content_type', 'object_id')
    
    # Request context
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    user_agent = models.CharField(max_length=500, blank=True, null=True)
    session_id = models.CharField(max_length=100, blank=True, null=True)
    request_method = models.CharField(max_length=10, blank=True, null=True)
    request_path = models.CharField(max_length=500, blank=True, null=True)
    request_params = models.JSONField(default=dict, blank=True)
    
    # Response information
    status_code = models.IntegerField(null=True, blank=True)
    response_time = models.FloatField(null=True, blank=True)  # in milliseconds
    
    # Event details
    description = models.TextField()
    additional_data = models.JSONField(default=dict, blank=True)
    
    # Security fields
    is_security_event = models.BooleanField(default=False)
    severity = models.CharField(max_length=20, choices=SecurityEventSeverity.choices, null=True, blank=True)
    requires_attention = models.BooleanField(default=False)
    
    # Timestamps
    timestamp = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'enhanced_audit_logs'
        ordering = ['-timestamp']
        indexes = [
            models.Index(fields=['user', 'timestamp']),
            models.Index(fields=['event_type', 'timestamp']),
            models.Index(fields=['module', 'timestamp']),
            models.Index(fields=['permission_checked', 'timestamp']),
            models.Index(fields=['is_security_event', 'timestamp']),
            models.Index(fields=['severity', 'timestamp']),
            models.Index(fields=['ip_address', 'timestamp']),
        ]
    
    def __str__(self):
        user_name = self.user.get_full_name() if self.user else 'Anonymous'
        return f"{user_name} - {self.event_type} - {self.action} - {self.timestamp}"
    
    @classmethod
    def log_permission_check(cls, user, permission, granted, request=None, **kwargs):
        """Log a permission check event"""
        return cls.objects.create(
            user=user,
            event_type=AuditEventType.PERMISSION_CHANGE if granted else AuditEventType.FAILED_ACCESS,
            action=f"check_permission_{permission}",
            module=kwargs.get('module', 'permissions'),
            permission_checked=permission,
            permission_granted=granted,
            role_at_time=user.role if user else None,
            branch_context=getattr(user, 'branch', {}).get('name') if user and hasattr(user, 'branch') else None,
            ip_address=cls._get_client_ip(request) if request else None,
            user_agent=request.META.get('HTTP_USER_AGENT', '') if request else None,
            session_id=request.session.session_key if request and hasattr(request, 'session') else None,
            request_method=request.method if request else None,
            request_path=request.path if request else None,
            description=f"Permission check for '{permission}' - {'Granted' if granted else 'Denied'}",
            is_security_event=not granted,
            severity=SecurityEventSeverity.MEDIUM if not granted else None,
            requires_attention=not granted,
            additional_data=kwargs
        )
    
    @classmethod
    def log_data_access(cls, user, object_type, object_id, action, request=None, **kwargs):
        """Log data access event"""
        return cls.objects.create(
            user=user,
            event_type=AuditEventType.DATA_ACCESS,
            action=action,
            module=kwargs.get('module', object_type.lower()),
            role_at_time=user.role if user else None,
            content_type=ContentType.objects.get_for_model(object_type) if hasattr(object_type, '_meta') else None,
            object_id=object_id,
            ip_address=cls._get_client_ip(request) if request else None,
            user_agent=request.META.get('HTTP_USER_AGENT', '') if request else None,
            session_id=request.session.session_key if request and hasattr(request, 'session') else None,
            request_method=request.method if request else None,
            request_path=request.path if request else None,
            description=f"Accessed {object_type.__name__ if hasattr(object_type, '__name__') else object_type} ID: {object_id}",
            additional_data=kwargs
        )
    
    @classmethod
    def log_security_event(cls, user, action, severity, description, request=None, **kwargs):
        """Log security event"""
        return cls.objects.create(
            user=user,
            event_type=AuditEventType.SECURITY_VIOLATION,
            action=action,
            module=kwargs.get('module', 'security'),
            role_at_time=user.role if user else None,
            ip_address=cls._get_client_ip(request) if request else None,
            user_agent=request.META.get('HTTP_USER_AGENT', '') if request else None,
            session_id=request.session.session_key if request and hasattr(request, 'session') else None,
            request_method=request.method if request else None,
            request_path=request.path if request else None,
            description=description,
            is_security_event=True,
            severity=severity,
            requires_attention=severity in [SecurityEventSeverity.HIGH, SecurityEventSeverity.CRITICAL],
            additional_data=kwargs
        )
    
    @staticmethod
    def _get_client_ip(request):
        """Get client IP address from request"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip


class PermissionChangeLog(models.Model):
    """
    Detailed logging for permission changes
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='permission_changes')
    changed_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='permission_changes_made')
    
    # Permission details
    permission_type = models.CharField(max_length=50)  # 'role', 'custom', 'template'
    permission_name = models.CharField(max_length=100)
    old_value = models.BooleanField(null=True, blank=True)
    new_value = models.BooleanField(null=True, blank=True)
    
    # Context
    reason = models.TextField(blank=True)
    approval_required = models.BooleanField(default=False)
    approved_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='permission_approvals')
    approved_at = models.DateTimeField(null=True, blank=True)
    
    # Metadata
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    session_id = models.CharField(max_length=100, blank=True, null=True)
    timestamp = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'permission_change_logs'
        ordering = ['-timestamp']
        indexes = [
            models.Index(fields=['user', 'timestamp']),
            models.Index(fields=['changed_by', 'timestamp']),
            models.Index(fields=['permission_name', 'timestamp']),
        ]
    
    def __str__(self):
        return f"{self.user.get_full_name()} - {self.permission_name} - {self.timestamp}"


class SecurityAlert(models.Model):
    """
    Security alerts for suspicious activities
    """
    ALERT_TYPES = [
        ('multiple_failed_logins', 'Multiple Failed Logins'),
        ('permission_escalation', 'Permission Escalation Attempt'),
        ('unusual_access_pattern', 'Unusual Access Pattern'),
        ('bulk_data_access', 'Bulk Data Access'),
        ('after_hours_access', 'After Hours Access'),
        ('suspicious_ip', 'Suspicious IP Address'),
        ('role_change', 'Role Change'),
        ('critical_permission_grant', 'Critical Permission Granted'),
    ]
    
    alert_type = models.CharField(max_length=50, choices=ALERT_TYPES)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='security_alerts', null=True, blank=True)
    severity = models.CharField(max_length=20, choices=SecurityEventSeverity.choices)
    
    title = models.CharField(max_length=200)
    description = models.TextField()
    
    # Context
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    user_agent = models.CharField(max_length=500, blank=True, null=True)
    additional_context = models.JSONField(default=dict, blank=True)
    
    # Status
    is_resolved = models.BooleanField(default=False)
    resolved_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='resolved_alerts')
    resolved_at = models.DateTimeField(null=True, blank=True)
    resolution_notes = models.TextField(blank=True)
    
    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'security_alerts'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['alert_type', 'created_at']),
            models.Index(fields=['severity', 'created_at']),
            models.Index(fields=['user', 'created_at']),
            models.Index(fields=['is_resolved', 'created_at']),
        ]
    
    def __str__(self):
        return f"{self.alert_type} - {self.severity} - {self.created_at}"
    
    def resolve(self, resolved_by, notes=""):
        """Mark alert as resolved"""
        self.is_resolved = True
        self.resolved_by = resolved_by
        self.resolved_at = timezone.now()
        self.resolution_notes = notes
        self.save()


class DataAccessPattern(models.Model):
    """
    Track data access patterns for anomaly detection
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='access_patterns')
    date = models.DateField()
    
    # Access metrics
    total_requests = models.IntegerField(default=0)
    unique_pages_accessed = models.IntegerField(default=0)
    data_exports = models.IntegerField(default=0)
    failed_access_attempts = models.IntegerField(default=0)
    
    # Time patterns
    first_access = models.TimeField(null=True, blank=True)
    last_access = models.TimeField(null=True, blank=True)
    after_hours_access = models.IntegerField(default=0)
    
    # Geographic patterns
    unique_ip_addresses = models.IntegerField(default=0)
    primary_ip = models.GenericIPAddressField(null=True, blank=True)
    
    # Behavioral flags
    is_anomalous = models.BooleanField(default=False)
    anomaly_score = models.FloatField(default=0.0)
    anomaly_reasons = models.JSONField(default=list, blank=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'data_access_patterns'
        unique_together = ['user', 'date']
        ordering = ['-date']
        indexes = [
            models.Index(fields=['user', 'date']),
            models.Index(fields=['is_anomalous', 'date']),
            models.Index(fields=['anomaly_score', 'date']),
        ]
    
    def __str__(self):
        return f"{self.user.get_full_name()} - {self.date} - Score: {self.anomaly_score}"