# -*- coding: utf-8 -*-
"""
Accounts Models

This module contains all the database models for the accounts application.
It implements a comprehensive user management system with role-based access control (RBAC),
permissions, and security features.

Models included:
- UserProfile: Extended user information and preferences
- Role: User roles for access control
- Permission: Granular permissions for system resources
- UserRole: Many-to-many relationship between users and roles
- PermissionGroup: Logical grouping of permissions
- UserSession: Session tracking for security
- UserAccess: External service credentials
- SecurityLog: Security event logging
- APIKey: API access management

Author: Adtlas Development Team
Version: 1.0.0
Created: 2024
"""

import uuid
import secrets
from datetime import timedelta
from typing import Optional, List, Dict, Any

from django.db import models
from django.contrib.auth.models import User, AbstractUser
from django.core.validators import RegexValidator, MinLengthValidator
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

from apps.core.models import BaseModel, TimeStampedModel


class UserProfile(BaseModel):
    """
    Extended user profile model that stores additional user information.
    
    This model extends the default Django User model with additional fields
    for user preferences, security settings, and profile information.
    It maintains a one-to-one relationship with the User model.
    
    Attributes:
        user (User): One-to-one relationship with Django User model
        user_type (str): Type of user (admin, manager, operator, viewer)
        phone (str): User's phone number with validation
        avatar (ImageField): User's profile picture
        timezone (str): User's preferred timezone
        language (str): User's preferred language
        department (str): User's department or team
        employee_id (str): Unique employee identifier
        date_of_birth (date): User's date of birth
        address (str): User's physical address
        emergency_contact (str): Emergency contact information
        
        # Security and Access Control
        api_key (str): Unique API key for programmatic access
        last_login_ip (GenericIPAddressField): IP address of last login
        failed_login_attempts (int): Number of consecutive failed login attempts
        account_locked_until (datetime): Account lockout expiration time
        password_changed_at (datetime): Last password change timestamp
        mfa_enabled (bool): Multi-factor authentication status
        mfa_secret (str): Secret key for MFA (encrypted)
        
        # Preferences and Settings
        email_notifications (bool): Email notification preference
        sms_notifications (bool): SMS notification preference
        dashboard_layout (str): Preferred dashboard layout
        items_per_page (int): Number of items to display per page
        
        # Status and Verification
        is_verified (bool): Account verification status
        verification_token (str): Token for account verification
        terms_accepted_at (datetime): Terms of service acceptance timestamp
        privacy_policy_accepted_at (datetime): Privacy policy acceptance timestamp
    """
    
    # User type choices for role-based access
    USER_TYPES = [
        ('admin', _('Administrator')),
        ('manager', _('Manager')),
        ('operator', _('Operator')),
        ('viewer', _('Viewer')),
        ('api_user', _('API User')),
        ('guest', _('Guest')),
    ]
    
    # Language choices
    LANGUAGE_CHOICES = [
        ('en', _('English')),
        ('fr', _('French')),
        ('ar', _('Arabic')),
        ('es', _('Spanish')),
    ]
    
    # Dashboard layout choices
    DASHBOARD_LAYOUTS = [
        ('grid', _('Grid Layout')),
        ('list', _('List Layout')),
        ('compact', _('Compact Layout')),
        ('detailed', _('Detailed Layout')),
    ]
    
    # Phone number validator
    phone_validator = RegexValidator(
        regex=r'^\+?1?\d{9,15}$',
        message=_('Phone number must be entered in the format: "+999999999". Up to 15 digits allowed.')
    )
    
    # Core user relationship
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        related_name='profile',
        verbose_name=_('User'),
        help_text=_('Associated Django user account')
    )
    
    # Basic profile information
    user_type = models.CharField(
        max_length=20,
        choices=USER_TYPES,
        default='viewer',
        verbose_name=_('User Type'),
        help_text=_('Type of user account for access control')
    )
    
    phone = models.CharField(
        max_length=20,
        blank=True,
        validators=[phone_validator],
        verbose_name=_('Phone Number'),
        help_text=_('User\'s contact phone number')
    )
    
    avatar = models.ImageField(
        upload_to='avatars/%Y/%m/',
        blank=True,
        null=True,
        verbose_name=_('Avatar'),
        help_text=_('User profile picture')
    )
    
    timezone = models.CharField(
        max_length=50,
        default='UTC',
        verbose_name=_('Timezone'),
        help_text=_('User\'s preferred timezone')
    )
    
    language = models.CharField(
        max_length=10,
        choices=LANGUAGE_CHOICES,
        default='en',
        verbose_name=_('Language'),
        help_text=_('User\'s preferred language')
    )
    
    department = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Department'),
        help_text=_('User\'s department or team')
    )
    
    employee_id = models.CharField(
        max_length=50,
        blank=True,
        unique=True,
        verbose_name=_('Employee ID'),
        help_text=_('Unique employee identifier')
    )
    
    date_of_birth = models.DateField(
        blank=True,
        null=True,
        verbose_name=_('Date of Birth'),
        help_text=_('User\'s date of birth')
    )
    
    address = models.TextField(
        blank=True,
        verbose_name=_('Address'),
        help_text=_('User\'s physical address')
    )
    
    emergency_contact = models.CharField(
        max_length=200,
        blank=True,
        verbose_name=_('Emergency Contact'),
        help_text=_('Emergency contact information')
    )
    
    # Security and access control fields
    api_key = models.CharField(
        max_length=255,
        blank=True,
        unique=True,
        verbose_name=_('API Key'),
        help_text=_('Unique API key for programmatic access')
    )
    
    last_login_ip = models.GenericIPAddressField(
        null=True,
        blank=True,
        verbose_name=_('Last Login IP'),
        help_text=_('IP address of the last login attempt')
    )
    
    failed_login_attempts = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Failed Login Attempts'),
        help_text=_('Number of consecutive failed login attempts')
    )
    
    account_locked_until = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('Account Locked Until'),
        help_text=_('Account lockout expiration time')
    )
    
    password_changed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('Password Changed At'),
        help_text=_('Timestamp of last password change')
    )
    
    mfa_enabled = models.BooleanField(
        default=False,
        verbose_name=_('MFA Enabled'),
        help_text=_('Multi-factor authentication status')
    )
    
    mfa_secret = models.CharField(
        max_length=255,
        blank=True,
        verbose_name=_('MFA Secret'),
        help_text=_('Secret key for multi-factor authentication')
    )
    
    # User preferences
    email_notifications = models.BooleanField(
        default=True,
        verbose_name=_('Email Notifications'),
        help_text=_('Receive email notifications')
    )
    
    sms_notifications = models.BooleanField(
        default=False,
        verbose_name=_('SMS Notifications'),
        help_text=_('Receive SMS notifications')
    )
    
    dashboard_layout = models.CharField(
        max_length=20,
        choices=DASHBOARD_LAYOUTS,
        default='grid',
        verbose_name=_('Dashboard Layout'),
        help_text=_('Preferred dashboard layout')
    )
    
    items_per_page = models.PositiveIntegerField(
        default=25,
        verbose_name=_('Items Per Page'),
        help_text=_('Number of items to display per page')
    )
    
    # Verification and compliance
    is_verified = models.BooleanField(
        default=False,
        verbose_name=_('Is Verified'),
        help_text=_('Account verification status')
    )
    
    verification_token = models.CharField(
        max_length=255,
        blank=True,
        verbose_name=_('Verification Token'),
        help_text=_('Token for account verification')
    )
    
    terms_accepted_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('Terms Accepted At'),
        help_text=_('Terms of service acceptance timestamp')
    )
    
    privacy_policy_accepted_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('Privacy Policy Accepted At'),
        help_text=_('Privacy policy acceptance timestamp')
    )
    
    class Meta:
        db_table = 'user_profiles'
        verbose_name = _('User Profile')
        verbose_name_plural = _('User Profiles')
        ordering = ['user__username']
        indexes = [
            models.Index(fields=['user_type']),
            models.Index(fields=['department']),
            models.Index(fields=['is_verified']),
            models.Index(fields=['employee_id']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the user profile.
        
        Returns:
            str: User's full name and type, or username if no full name
        """
        full_name = self.user.get_full_name()
        if full_name:
            return f"{full_name} ({self.get_user_type_display()})"
        return f"{self.user.username} ({self.get_user_type_display()})"
    
    def save(self, *args, **kwargs):
        """
        Override save method to generate API key if not present.
        
        This method automatically generates a unique API key for the user
        if one doesn't already exist. It also performs validation on
        certain fields before saving.
        
        Args:
            *args: Variable length argument list
            **kwargs: Arbitrary keyword arguments
        """
        # Generate API key if not present
        if not self.api_key:
            self.api_key = self.generate_api_key()
        
        # Validate items per page range
        if self.items_per_page < 10 or self.items_per_page > 100:
            self.items_per_page = 25
        
        super().save(*args, **kwargs)
    
    @staticmethod
    def generate_api_key() -> str:
        """
        Generate a secure API key for the user.
        
        Returns:
            str: A secure, URL-safe API key
        """
        return secrets.token_urlsafe(32)
    
    @property
    def full_name(self) -> str:
        """
        Get the user's full name or username as fallback.
        
        Returns:
            str: User's full name or username
        """
        return self.user.get_full_name() or self.user.username
    
    @property
    def is_account_locked(self) -> bool:
        """
        Check if the user account is currently locked.
        
        Returns:
            bool: True if account is locked, False otherwise
        """
        if self.account_locked_until:
            return self.account_locked_until > timezone.now()
        return False
    
    @property
    def password_age_days(self) -> Optional[int]:
        """
        Calculate the age of the current password in days.
        
        Returns:
            Optional[int]: Number of days since password was changed, or None
        """
        if self.password_changed_at:
            return (timezone.now() - self.password_changed_at).days
        return None
    
    def can_manage_campaigns(self) -> bool:
        """
        Check if user can manage campaigns.
        
        Returns:
            bool: True if user can manage campaigns
        """
        return self.user_type in ['admin', 'manager']
    
    def can_manage_users(self) -> bool:
        """
        Check if user can manage other users.
        
        Returns:
            bool: True if user can manage users
        """
        return self.user_type == 'admin'
    
    def can_view_analytics(self) -> bool:
        """
        Check if user can view analytics.
        
        Returns:
            bool: True if user can view analytics
        """
        return self.user_type in ['admin', 'manager', 'operator']
    
    def can_access_api(self) -> bool:
        """
        Check if user can access API endpoints.
        
        Returns:
            bool: True if user can access API
        """
        return bool(self.api_key) and self.user.is_active
    
    def lock_account(self, duration_minutes: int = 30) -> None:
        """
        Lock the user account for a specified duration.
        
        Args:
            duration_minutes (int): Duration to lock account in minutes
        """
        self.account_locked_until = timezone.now() + timedelta(minutes=duration_minutes)
        self.save(update_fields=['account_locked_until'])
    
    def unlock_account(self) -> None:
        """
        Unlock the user 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 regenerate_api_key(self) -> str:
        """
        Generate a new API key for the user.
        
        Returns:
            str: The new API key
        """
        self.api_key = self.generate_api_key()
        self.save(update_fields=['api_key'])
        return self.api_key


class Role(BaseModel):
    """
    Role model for role-based access control (RBAC).
    
    This model defines roles that can be assigned to users to control
    access to different parts of the system. Roles can have hierarchical
    relationships and can inherit permissions from parent roles.
    
    Attributes:
        name (str): Unique name of the role
        display_name (str): Human-readable display name
        description (str): Detailed description of the role
        is_active (bool): Whether the role is currently active
        is_system_role (bool): Whether this is a system-defined role
        parent_role (Role): Parent role for hierarchy (optional)
        priority (int): Priority level for role ordering
        max_users (int): Maximum number of users that can have this role
        expiry_days (int): Number of days before role assignment expires
    """
    
    name = models.CharField(
        max_length=50,
        unique=True,
        validators=[RegexValidator(
            regex=r'^[a-zA-Z0-9_]+$',
            message=_('Role name can only contain letters, numbers, and underscores')
        )],
        verbose_name=_('Role Name'),
        help_text=_('Unique identifier for the role')
    )
    
    display_name = models.CharField(
        max_length=100,
        verbose_name=_('Display Name'),
        help_text=_('Human-readable name for the role')
    )
    
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the role and its permissions')
    )
    
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Is Active'),
        help_text=_('Whether this role is currently active')
    )
    
    is_system_role = models.BooleanField(
        default=False,
        verbose_name=_('Is System Role'),
        help_text=_('Whether this is a system-defined role that cannot be deleted')
    )
    
    parent_role = models.ForeignKey(
        'self',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='child_roles',
        verbose_name=_('Parent Role'),
        help_text=_('Parent role for hierarchical permissions')
    )
    
    priority = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Priority'),
        help_text=_('Priority level for role ordering (higher = more important)')
    )
    
    max_users = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_('Maximum Users'),
        help_text=_('Maximum number of users that can have this role')
    )
    
    expiry_days = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_('Expiry Days'),
        help_text=_('Number of days before role assignment expires')
    )
    
    class Meta:
        db_table = 'roles'
        verbose_name = _('Role')
        verbose_name_plural = _('Roles')
        ordering = ['-priority', 'display_name']
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['is_active']),
            models.Index(fields=['priority']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the role.
        
        Returns:
            str: Display name of the role
        """
        return self.display_name
    
    def clean(self):
        """
        Validate the role data.
        
        Raises:
            ValidationError: If validation fails
        """
        super().clean()
        
        # Prevent circular parent relationships
        if self.parent_role:
            if self.parent_role == self:
                raise ValidationError(_('A role cannot be its own parent'))
            
            # Check for circular references
            parent = self.parent_role
            while parent:
                if parent == self:
                    raise ValidationError(_('Circular parent relationship detected'))
                parent = parent.parent_role
    
    def get_all_permissions(self) -> List['Permission']:
        """
        Get all permissions for this role, including inherited ones.
        
        Returns:
            List[Permission]: All permissions for this role
        """
        permissions = list(self.permissions.filter(is_active=True))
        
        # Add permissions from parent roles
        if self.parent_role:
            permissions.extend(self.parent_role.get_all_permissions())
        
        # Remove duplicates while preserving order
        seen = set()
        unique_permissions = []
        for perm in permissions:
            if perm.id not in seen:
                seen.add(perm.id)
                unique_permissions.append(perm)
        
        return unique_permissions
    
    def has_permission(self, permission_codename: str) -> bool:
        """
        Check if this role has a specific permission.
        
        Args:
            permission_codename (str): The permission codename to check
        
        Returns:
            bool: True if role has the permission
        """
        return any(
            perm.codename == permission_codename
            for perm in self.get_all_permissions()
        )
    
    def get_user_count(self) -> int:
        """
        Get the number of users assigned to this role.
        
        Returns:
            int: Number of users with this role
        """
        return self.user_roles.filter(is_active=True).count()
    
    def can_assign_to_user(self) -> bool:
        """
        Check if this role can be assigned to more users.
        
        Returns:
            bool: True if role can be assigned to more users
        """
        if not self.is_active:
            return False
        
        if self.max_users is None:
            return True
        
        return self.get_user_count() < self.max_users


class Permission(BaseModel):
    """
    Permission model for granular access control.
    
    This model defines specific permissions that can be granted to roles
    or users. Permissions are organized by resource and action, allowing
    for fine-grained access control throughout the system.
    
    Attributes:
        codename (str): Unique permission identifier
        name (str): Human-readable permission name
        description (str): Detailed description of what the permission allows
        resource (str): The resource this permission applies to
        action (str): The action this permission allows
        is_active (bool): Whether the permission is currently active
        is_system_permission (bool): Whether this is a system permission
        content_type (ContentType): Optional content type for object-level permissions
        priority (int): Priority level for permission ordering
    """
    
    # Action choices for permissions
    ACTION_CHOICES = [
        ('create', _('Create')),
        ('read', _('Read')),
        ('update', _('Update')),
        ('delete', _('Delete')),
        ('list', _('List')),
        ('export', _('Export')),
        ('import', _('Import')),
        ('approve', _('Approve')),
        ('reject', _('Reject')),
        ('publish', _('Publish')),
        ('archive', _('Archive')),
        ('restore', _('Restore')),
        ('manage', _('Manage')),
        ('admin', _('Admin')),
    ]
    
    # Resource choices
    RESOURCE_CHOICES = [
        ('users', _('Users')),
        ('roles', _('Roles')),
        ('permissions', _('Permissions')),
        ('campaigns', _('Campaigns')),
        ('advertisers', _('Advertisers')),
        ('channels', _('Channels')),
        ('analytics', _('Analytics')),
        ('vast', _('VAST Templates')),
        ('reports', _('Reports')),
        ('settings', _('Settings')),
        ('logs', _('Logs')),
        ('api', _('API')),
        ('system', _('System')),
    ]
    
    codename = models.CharField(
        max_length=100,
        unique=True,
        validators=[RegexValidator(
            regex=r'^[a-zA-Z0-9_\.]+$',
            message=_('Permission codename can only contain letters, numbers, underscores, and dots')
        )],
        verbose_name=_('Codename'),
        help_text=_('Unique identifier for the permission')
    )
    
    name = models.CharField(
        max_length=255,
        verbose_name=_('Name'),
        help_text=_('Human-readable name for the permission')
    )
    
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of what this permission allows')
    )
    
    resource = models.CharField(
        max_length=50,
        choices=RESOURCE_CHOICES,
        verbose_name=_('Resource'),
        help_text=_('The resource this permission applies to')
    )
    
    action = models.CharField(
        max_length=20,
        choices=ACTION_CHOICES,
        verbose_name=_('Action'),
        help_text=_('The action this permission allows')
    )
    
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Is Active'),
        help_text=_('Whether this permission is currently active')
    )
    
    is_system_permission = models.BooleanField(
        default=False,
        verbose_name=_('Is System Permission'),
        help_text=_('Whether this is a system permission that cannot be deleted')
    )
    
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('Content Type'),
        help_text=_('Content type for object-level permissions')
    )
    
    priority = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Priority'),
        help_text=_('Priority level for permission ordering')
    )
    
    class Meta:
        db_table = 'permissions'
        verbose_name = _('Permission')
        verbose_name_plural = _('Permissions')
        ordering = ['resource', 'action', 'name']
        unique_together = [['resource', 'action', 'content_type']]
        indexes = [
            models.Index(fields=['codename']),
            models.Index(fields=['resource', 'action']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the permission.
        
        Returns:
            str: Permission name
        """
        return self.name
    
    def save(self, *args, **kwargs):
        """
        Override save method to auto-generate codename if not provided.
        
        Args:
            *args: Variable length argument list
            **kwargs: Arbitrary keyword arguments
        """
        if not self.codename:
            self.codename = f"{self.resource}.{self.action}"
            if self.content_type:
                self.codename += f".{self.content_type.model}"
        
        super().save(*args, **kwargs)


class UserRole(TimeStampedModel):
    """
    Many-to-many relationship model between users and roles.
    
    This model tracks the assignment of roles to users, including
    assignment dates, expiry dates, and assignment metadata.
    
    Attributes:
        user (User): The user assigned to the role
        role (Role): The role assigned to the user
        assigned_by (User): User who made the assignment
        assigned_at (datetime): When the role was assigned
        expires_at (datetime): When the role assignment expires
        is_active (bool): Whether the assignment is currently active
        notes (str): Additional notes about the assignment
    """
    
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='user_roles',
        verbose_name=_('User'),
        help_text=_('User assigned to the role')
    )
    
    role = models.ForeignKey(
        Role,
        on_delete=models.CASCADE,
        related_name='user_roles',
        verbose_name=_('Role'),
        help_text=_('Role assigned to the user')
    )
    
    assigned_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='role_assignments_made',
        verbose_name=_('Assigned By'),
        help_text=_('User who made this role assignment')
    )
    
    assigned_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('Assigned At'),
        help_text=_('When the role was assigned')
    )
    
    expires_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('Expires At'),
        help_text=_('When the role assignment expires')
    )
    
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Is Active'),
        help_text=_('Whether this role assignment is currently active')
    )
    
    notes = models.TextField(
        blank=True,
        verbose_name=_('Notes'),
        help_text=_('Additional notes about this role assignment')
    )
    
    class Meta:
        db_table = 'user_roles'
        verbose_name = _('User Role')
        verbose_name_plural = _('User Roles')
        unique_together = [['user', 'role']]
        ordering = ['-assigned_at']
        indexes = [
            models.Index(fields=['user', 'is_active']),
            models.Index(fields=['role', 'is_active']),
            models.Index(fields=['expires_at']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the user role assignment.
        
        Returns:
            str: User and role assignment description
        """
        return f"{self.user.username} - {self.role.display_name}"
    
    @property
    def is_expired(self) -> bool:
        """
        Check if the role assignment has expired.
        
        Returns:
            bool: True if the assignment has expired
        """
        if self.expires_at:
            return self.expires_at <= timezone.now()
        return False
    
    def clean(self):
        """
        Validate the user role assignment.
        
        Raises:
            ValidationError: If validation fails
        """
        super().clean()
        
        # Check if role can be assigned to more users
        if not self.role.can_assign_to_user():
            raise ValidationError(
                _('This role has reached its maximum user limit')
            )
        
        # Validate expiry date
        if self.expires_at and self.expires_at <= timezone.now():
            raise ValidationError(
                _('Expiry date must be in the future')
            )


class RolePermission(TimeStampedModel):
    """
    Many-to-many relationship model between roles and permissions.
    
    This model tracks which permissions are granted to which roles,
    including grant dates and metadata.
    
    Attributes:
        role (Role): The role granted the permission
        permission (Permission): The permission granted to the role
        granted_by (User): User who granted the permission
        granted_at (datetime): When the permission was granted
        is_active (bool): Whether the grant is currently active
        notes (str): Additional notes about the grant
    """
    
    role = models.ForeignKey(
        Role,
        on_delete=models.CASCADE,
        related_name='permissions',
        verbose_name=_('Role'),
        help_text=_('Role granted the permission')
    )
    
    permission = models.ForeignKey(
        Permission,
        on_delete=models.CASCADE,
        related_name='roles',
        verbose_name=_('Permission'),
        help_text=_('Permission granted to the role')
    )
    
    granted_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='permission_grants_made',
        verbose_name=_('Granted By'),
        help_text=_('User who granted this permission')
    )
    
    granted_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('Granted At'),
        help_text=_('When the permission was granted')
    )
    
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Is Active'),
        help_text=_('Whether this permission grant is currently active')
    )
    
    notes = models.TextField(
        blank=True,
        verbose_name=_('Notes'),
        help_text=_('Additional notes about this permission grant')
    )
    
    class Meta:
        db_table = 'role_permissions'
        verbose_name = _('Role Permission')
        verbose_name_plural = _('Role Permissions')
        unique_together = [['role', 'permission']]
        ordering = ['-granted_at']
        indexes = [
            models.Index(fields=['role', 'is_active']),
            models.Index(fields=['permission', 'is_active']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the role permission grant.
        
        Returns:
            str: Role and permission grant description
        """
        return f"{self.role.display_name} - {self.permission.name}"


# Continue with remaining models in the next part due to length constraints...