"""
Data Policy Models - Row-level security and data visibility.

This module provides:
- Row-Level Security (RLS): Control which records users can see/modify
- Data Visibility Policies: Define rules for data access
- Data Scope: Automatic filtering based on user context

Key concepts:
- Policy: A rule that defines data access conditions
- Scope: The context (org unit, department, etc.) that affects visibility
- Filter: The actual query condition applied to data access
"""
from django.db import models
from django.core.validators import RegexValidator

from apps.core.tenant.models import TenantAwareModel


class PolicyType(models.TextChoices):
    """Types of data policies."""
    ROW_FILTER = 'row_filter', 'Row Filter (Which records)'
    FIELD_MASK = 'field_mask', 'Field Mask (Which fields)'
    AGGREGATE_ONLY = 'aggregate_only', 'Aggregate Only (No details)'
    TIME_BASED = 'time_based', 'Time Based Access'
    CONDITIONAL = 'conditional', 'Conditional Access'


class PolicyAction(models.TextChoices):
    """Actions the policy applies to."""
    READ = 'read', 'Read'
    WRITE = 'write', 'Write'
    DELETE = 'delete', 'Delete'
    ALL = 'all', 'All Operations'


class DataPolicy(TenantAwareModel):
    """
    Data Policy - Defines row-level security rules.
    
    A policy defines:
    - What data (module, resource)
    - Who can access (role, conditions)
    - What access (read, write, delete)
    - Under what conditions (filters)
    
    Example policies:
    
    1. "Users can only see invoices from their department"
       - resource: accounting.invoice
       - condition: record.department_id IN user.departments
       
    2. "Managers can see all data in their branch"
       - resource: *
       - condition: record.branch_id IN user.managed_branches
       
    3. "Salary data is only visible to HR and the employee"
       - resource: hrm.employee
       - field_restrictions: {salary: owner_or_hr}
    """
    
    # Policy identification
    name = models.CharField(
        max_length=100,
        help_text="Policy name"
    )
    slug = models.SlugField(
        max_length=100,
        help_text="Unique identifier"
    )
    description = models.TextField(blank=True)
    
    # What this policy applies to
    module = models.CharField(
        max_length=100,
        help_text="Module name (* for all modules)"
    )
    resource = models.CharField(
        max_length=100,
        help_text="Resource name (* for all resources)"
    )
    
    # Policy type
    policy_type = models.CharField(
        max_length=20,
        choices=PolicyType.choices,
        default=PolicyType.ROW_FILTER
    )
    
    # Actions this policy applies to
    actions = models.JSONField(
        default=list,
        help_text="List of actions: ['read', 'write', 'delete'] or ['all']"
    )
    
    # Who this policy applies to
    # If null, applies to all users
    applies_to_roles = models.ManyToManyField(
        'core_permission.Role',
        blank=True,
        related_name='data_policies',
        help_text="Roles this policy applies to (empty = all roles)"
    )
    
    # Filter conditions (JSON)
    # Format varies by policy type:
    #
    # ROW_FILTER:
    # {
    #     "type": "org_unit",  // or "custom", "owner", "team"
    #     "field": "department_id",  // field on the model
    #     "include_descendants": true,
    #     "custom_filter": null  // or Q-object serialization for custom
    # }
    #
    # FIELD_MASK:
    # {
    #     "fields": {
    #         "salary": {"visibility": "owner_or_role", "roles": ["hr_admin"]},
    #         "ssn": {"visibility": "masked", "mask": "***-**-{last4}"}
    #     }
    # }
    #
    # TIME_BASED:
    # {
    #     "allowed_days": [1, 2, 3, 4, 5],  // Monday-Friday
    #     "allowed_hours": {"start": 8, "end": 18},
    #     "timezone": "Asia/Tehran"
    # }
    conditions = models.JSONField(
        default=dict,
        help_text="Policy conditions (JSON)"
    )
    
    # Priority (higher = evaluated first)
    priority = models.IntegerField(
        default=0,
        help_text="Higher priority policies are evaluated first"
    )
    
    # Is this an allow or deny policy?
    is_allow = models.BooleanField(
        default=True,
        help_text="True = allow access, False = deny access"
    )
    
    # Status
    is_active = models.BooleanField(default=True)
    
    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'data_policies'
        unique_together = ['tenant', 'slug']
        verbose_name = 'Data Policy'
        verbose_name_plural = 'Data Policies'
        ordering = ['-priority', 'name']
    
    def __str__(self):
        return f"{self.name} ({self.module}.{self.resource})"


class DataScope(TenantAwareModel):
    """
    Data Scope - Predefined scope templates.
    
    A scope is a reusable filter configuration that can be
    applied to multiple policies or assigned directly to users/roles.
    
    Example scopes:
    - "Own Records Only": user_id = current_user
    - "Department": department_id IN user.departments
    - "Branch": branch_id IN user.branches
    - "Team": team_id IN user.teams
    """
    
    name = models.CharField(max_length=100)
    slug = models.SlugField(max_length=100)
    description = models.TextField(blank=True)
    
    # Scope type
    scope_type = models.CharField(
        max_length=50,
        choices=[
            ('owner', 'Owner Only (own records)'),
            ('org_unit', 'Organizational Unit'),
            ('team', 'Team Members'),
            ('custom', 'Custom Filter'),
        ],
        default='org_unit'
    )
    
    # Configuration
    # For org_unit type:
    # {
    #     "org_unit_field": "department_id",  // field name on target model
    #     "user_org_unit_relation": "organizational_units",  // how to get user's units
    #     "include_descendants": true
    # }
    #
    # For owner type:
    # {
    #     "owner_field": "created_by_id",  // or "user_id", "owner_id"
    # }
    #
    # For custom type:
    # {
    #     "filter_expression": "Q(status='active') | Q(created_by=user)",
    #     "parameters": ["user"]
    # }
    configuration = models.JSONField(
        default=dict,
        help_text="Scope configuration"
    )
    
    is_active = models.BooleanField(default=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'data_scopes'
        unique_together = ['tenant', 'slug']
        verbose_name = 'Data Scope'
        verbose_name_plural = 'Data Scopes'
    
    def __str__(self):
        return self.name


class UserDataScope(models.Model):
    """
    User Data Scope assignment.
    
    Assigns a scope to a specific user, optionally for
    specific modules/resources.
    """
    
    user = models.ForeignKey(
        'core_auth.User',
        on_delete=models.CASCADE,
        related_name='data_scopes'
    )
    scope = models.ForeignKey(
        DataScope,
        on_delete=models.CASCADE,
        related_name='user_assignments'
    )
    
    # Optional: limit to specific module/resource
    module = models.CharField(
        max_length=100,
        blank=True,
        help_text="Module this scope applies to (* or empty = all)"
    )
    resource = models.CharField(
        max_length=100,
        blank=True,
        help_text="Resource this scope applies to (* or empty = all)"
    )
    
    # Additional parameters for the scope
    parameters = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional parameters for scope evaluation"
    )
    
    is_active = models.BooleanField(default=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'user_data_scopes'
        verbose_name = 'User Data Scope'
        verbose_name_plural = 'User Data Scopes'
    
    def __str__(self):
        return f"{self.user} - {self.scope}"


class RoleDataScope(models.Model):
    """
    Role Data Scope assignment.
    
    Assigns a scope to a role - all users with this role
    will have this scope applied.
    """
    
    role = models.ForeignKey(
        'core_permission.Role',
        on_delete=models.CASCADE,
        related_name='data_scopes'
    )
    scope = models.ForeignKey(
        DataScope,
        on_delete=models.CASCADE,
        related_name='role_assignments'
    )
    
    # Optional: limit to specific module/resource
    module = models.CharField(
        max_length=100,
        blank=True
    )
    resource = models.CharField(
        max_length=100,
        blank=True
    )
    
    parameters = models.JSONField(default=dict, blank=True)
    
    is_active = models.BooleanField(default=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'role_data_scopes'
        verbose_name = 'Role Data Scope'
        verbose_name_plural = 'Role Data Scopes'
    
    def __str__(self):
        return f"{self.role} - {self.scope}"


class DataAccessLog(TenantAwareModel):
    """
    Data Access Log - Audit trail for sensitive data access.
    
    Logs access to sensitive data for compliance and auditing.
    Only logs access that matches defined sensitivity rules.
    """
    
    # Who accessed
    user = models.ForeignKey(
        'core_auth.User',
        on_delete=models.SET_NULL,
        null=True,
        related_name='data_access_logs'
    )
    
    # What was accessed
    module = models.CharField(max_length=100)
    resource = models.CharField(max_length=100)
    record_id = models.CharField(max_length=100)
    
    # Access details
    action = models.CharField(max_length=50)  # read, update, delete
    fields_accessed = models.JSONField(
        default=list,
        help_text="List of fields accessed"
    )
    
    # Context
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    user_agent = models.TextField(blank=True)
    
    # Result
    was_allowed = models.BooleanField(default=True)
    policy_applied = models.CharField(
        max_length=100,
        blank=True,
        help_text="Name of the policy that allowed/denied access"
    )
    
    # Timestamp
    accessed_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'data_access_logs'
        verbose_name = 'Data Access Log'
        verbose_name_plural = 'Data Access Logs'
        indexes = [
            models.Index(fields=['tenant', 'user', 'accessed_at']),
            models.Index(fields=['tenant', 'module', 'resource']),
            models.Index(fields=['accessed_at']),
        ]
    
    def __str__(self):
        return f"{self.user} - {self.module}.{self.resource}:{self.record_id}"
