"""
Enhanced Permission Models for Granular Page-Specific Permissions System
"""
from django.db import models
from django.utils import timezone
from django.core.validators import RegexValidator
from utils.datetime_utils import get_current_datetime
import uuid


class PagePermission(models.Model):
    """
    Page-specific permission definitions with granular action controls
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    # Page and action identification
    page_name = models.CharField(
        max_length=100,
        help_text="Name of the page (e.g., 'loans', 'clients', 'reports')"
    )
    action_code = models.CharField(
        max_length=50,
        help_text="Unique code for the action (e.g., 'loans_create_application')"
    )
    action_name = models.CharField(
        max_length=200,
        help_text="Human-readable name for the action"
    )
    description = models.TextField(
        help_text="Detailed description of what this permission allows"
    )
    
    # Categorization and security
    category = models.CharField(
        max_length=50,
        choices=[
            ('view', 'View/Access'),
            ('create', 'Create/Add'),
            ('edit', 'Edit/Modify'),
            ('delete', 'Delete/Remove'),
            ('approve', 'Approve/Reject'),
            ('export', 'Export/Download'),
            ('manage', 'Manage/Configure'),
            ('process', 'Process/Execute'),
        ],
        help_text="Category of permission for grouping"
    )
    is_critical = models.BooleanField(
        default=False,
        help_text="Whether this is a critical permission requiring special approval"
    )
    
    # Dependencies and requirements
    required_permissions = models.ManyToManyField(
        'self',
        blank=True,
        symmetrical=False,
        help_text="Other permissions required for this permission to be effective"
    )
    
    # Metadata
    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 = 'page_permissions'
        unique_together = ['page_name', 'action_code']
        ordering = ['page_name', 'category', 'action_name']
        indexes = [
            models.Index(fields=['page_name', 'category']),
            models.Index(fields=['action_code']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        return f"{self.page_name}: {self.action_name}"
    
    def get_full_permission_code(self):
        """Get the full permission code for this permission"""
        return f"{self.page_name}_{self.action_code}"
    
    def is_dependency_satisfied(self, user):
        """Check if all required permissions are satisfied for this user"""
        if not self.required_permissions.exists():
            return True
        
        for required_perm in self.required_permissions.all():
            if not user.has_page_permission(required_perm.page_name, required_perm.action_code):
                return False
        return True


class RolePermissionTemplate(models.Model):
    """
    Default permission templates for roles - defines what permissions each role gets by default
    """
    ROLE_CHOICES = [
        ('admin', 'Admin'),
        ('team_leader', 'Team Leader'),
        ('loan_officer', 'Loan Officer'),
        ('secretary', 'Secretary'),
        ('auditor', 'Auditor'),
        ('borrower', 'Borrower'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    role = models.CharField(
        max_length=20, 
        choices=ROLE_CHOICES,
        help_text="Role this template applies to"
    )
    page_permission = models.ForeignKey(
        PagePermission, 
        on_delete=models.CASCADE,
        help_text="The specific page permission"
    )
    is_allowed = models.BooleanField(
        default=False,
        help_text="Whether this role has this permission by default"
    )
    can_override = models.BooleanField(
        default=True,
        help_text="Whether individual users can override this permission"
    )
    priority = models.IntegerField(
        default=0,
        help_text="Priority for permission resolution (higher = more important)"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_role_templates'
    )
    
    class Meta:
        db_table = 'role_permission_templates'
        unique_together = ['role', 'page_permission']
        ordering = ['role', 'page_permission__page_name', 'page_permission__category']
        indexes = [
            models.Index(fields=['role']),
            models.Index(fields=['is_allowed']),
            models.Index(fields=['can_override']),
        ]
    
    def __str__(self):
        status = "✓" if self.is_allowed else "✗"
        override = " (Override)" if self.can_override else " (Fixed)"
        return f"{self.get_role_display()}: {self.page_permission.action_name} {status}{override}"


class UserPagePermission(models.Model):
    """
    Custom user permission overrides - allows individual users to have different permissions than their role default
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    user = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.CASCADE,
        related_name='custom_page_permissions',
        help_text="User this custom permission applies to"
    )
    page_permission = models.ForeignKey(
        PagePermission,
        on_delete=models.CASCADE,
        help_text="The specific page permission being overridden"
    )
    is_allowed = models.BooleanField(
        help_text="Whether this user has this permission (overrides role default)"
    )
    
    # Authorization and tracking
    granted_by = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='granted_page_permissions',
        help_text="User who granted this permission override"
    )
    reason = models.TextField(
        blank=True,
        help_text="Reason for granting this permission override"
    )
    
    # Expiration
    expires_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When this permission override expires (null = never expires)"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'user_page_permissions'
        unique_together = ['user', 'page_permission']
        ordering = ['user__username', 'page_permission__page_name', 'page_permission__category']
        indexes = [
            models.Index(fields=['user', 'page_permission']),
            models.Index(fields=['expires_at']),
            models.Index(fields=['is_allowed']),
            models.Index(fields=['granted_by']),
        ]
    
    def __str__(self):
        status = "✓" if self.is_allowed else "✗"
        expiry = f" (expires {self.expires_at.strftime('%Y-%m-%d')})" if self.expires_at else ""
        return f"{self.user.get_full_name()}: {self.page_permission.action_name} {status}{expiry}"
    
    def is_expired(self):
        """Check if this permission override has expired"""
        if not self.expires_at:
            return False
        return timezone.now() > self.expires_at
    
    def days_until_expiry(self):
        """Get number of days until this permission expires"""
        if not self.expires_at:
            return None
        delta = self.expires_at - timezone.now()
        return delta.days if delta.days > 0 else 0
    
    def extend_expiry(self, days):
        """Extend the expiry date by specified number of days"""
        if self.expires_at:
            self.expires_at += timezone.timedelta(days=days)
        else:
            self.expires_at = timezone.now() + timezone.timedelta(days=days)
        self.save()

# Analytics Models for Portfolio and Client Growth Tracking

class PortfolioSnapshot(models.Model):
    """
    Daily portfolio snapshots for trend analysis and performance tracking
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    # Portfolio manager and date
    manager = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.CASCADE,
        related_name='portfolio_snapshots',
        limit_choices_to={'role__in': ['loan_officer', 'team_leader']},
        help_text="Portfolio manager this snapshot belongs to"
    )
    branch = models.ForeignKey(
        'users.Branch',
        on_delete=models.CASCADE,
        related_name='portfolio_snapshots',
        help_text="Branch this snapshot belongs to"
    )
    snapshot_date = models.DateField(
        help_text="Date of this portfolio snapshot"
    )
    
    # Client metrics
    total_clients = models.IntegerField(
        default=0,
        help_text="Total number of clients in portfolio"
    )
    active_clients = models.IntegerField(
        default=0,
        help_text="Number of active clients"
    )
    new_clients = models.IntegerField(
        default=0,
        help_text="New clients added on this date"
    )
    churned_clients = models.IntegerField(
        default=0,
        help_text="Clients who churned on this date"
    )
    
    # Loan metrics
    active_loans = models.IntegerField(
        default=0,
        help_text="Number of active loans"
    )
    total_loans = models.IntegerField(
        default=0,
        help_text="Total number of loans ever issued"
    )
    new_loans = models.IntegerField(
        default=0,
        help_text="New loans disbursed on this date"
    )
    completed_loans = models.IntegerField(
        default=0,
        help_text="Loans completed on this date"
    )
    defaulted_loans = models.IntegerField(
        default=0,
        help_text="Loans that defaulted on this date"
    )
    
    # Financial metrics
    total_disbursed = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Total amount disbursed to date"
    )
    total_outstanding = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Total outstanding amount"
    )
    total_collected = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Total amount collected to date"
    )
    daily_disbursements = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Amount disbursed on this date"
    )
    daily_collections = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Amount collected on this date"
    )
    
    # Risk metrics
    par_30 = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Portfolio at Risk (30+ days overdue)"
    )
    par_60 = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Portfolio at Risk (60+ days overdue)"
    )
    par_90 = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Portfolio at Risk (90+ days overdue)"
    )
    default_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=0,
        help_text="Default rate as percentage"
    )
    
    # Performance metrics
    collection_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=0,
        help_text="Collection rate as percentage"
    )
    portfolio_yield = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=0,
        help_text="Portfolio yield as percentage"
    )
    average_loan_size = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=0,
        help_text="Average loan size"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'portfolio_snapshots'
        unique_together = ['manager', 'snapshot_date']
        ordering = ['-snapshot_date', 'manager__username']
        indexes = [
            models.Index(fields=['manager', 'snapshot_date']),
            models.Index(fields=['branch', 'snapshot_date']),
            models.Index(fields=['snapshot_date']),
            models.Index(fields=['-snapshot_date']),
        ]
    
    def __str__(self):
        return f"{self.manager.get_full_name()} - {self.snapshot_date}"
    
    def calculate_growth_rate(self, previous_snapshot=None):
        """Calculate growth rate compared to previous snapshot"""
        if not previous_snapshot:
            # Get previous snapshot
            previous_snapshot = PortfolioSnapshot.objects.filter(
                manager=self.manager,
                snapshot_date__lt=self.snapshot_date
            ).order_by('-snapshot_date').first()
        
        if not previous_snapshot or previous_snapshot.total_clients == 0:
            return 0
        
        growth = ((self.total_clients - previous_snapshot.total_clients) / 
                 previous_snapshot.total_clients) * 100
        return round(growth, 2)
    
    def get_portfolio_health_score(self):
        """Calculate overall portfolio health score (0-100)"""
        # Factors: collection rate (40%), default rate (30%), growth (20%), yield (10%)
        collection_score = min(self.collection_rate, 100) * 0.4
        default_score = max(0, 100 - (self.default_rate * 10)) * 0.3  # Lower default rate = higher score
        
        # Calculate growth score
        growth_rate = self.calculate_growth_rate()
        growth_score = min(max(growth_rate + 50, 0), 100) * 0.2  # Normalize around 0% growth
        
        yield_score = min(self.portfolio_yield * 5, 100) * 0.1  # Assume 20% yield = 100 score
        
        total_score = collection_score + default_score + growth_score + yield_score
        return round(total_score, 1)


class ClientGrowthMetrics(models.Model):
    """
    Client growth tracking and analytics by branch and time period
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    # Location and time period
    branch = models.ForeignKey(
        'users.Branch',
        on_delete=models.CASCADE,
        related_name='growth_metrics',
        help_text="Branch this metric belongs to"
    )
    period_start = models.DateField(
        help_text="Start date of the measurement period"
    )
    period_end = models.DateField(
        help_text="End date of the measurement period"
    )
    period_type = models.CharField(
        max_length=20,
        choices=[
            ('daily', 'Daily'),
            ('weekly', 'Weekly'),
            ('monthly', 'Monthly'),
            ('quarterly', 'Quarterly'),
            ('yearly', 'Yearly'),
        ],
        default='monthly',
        help_text="Type of period this metric covers"
    )
    
    # Client acquisition metrics
    new_clients = models.IntegerField(
        default=0,
        help_text="Number of new clients acquired in this period"
    )
    churned_clients = models.IntegerField(
        default=0,
        help_text="Number of clients who churned in this period"
    )
    reactivated_clients = models.IntegerField(
        default=0,
        help_text="Number of previously inactive clients who became active"
    )
    total_clients = models.IntegerField(
        default=0,
        help_text="Total number of clients at end of period"
    )
    
    # Financial metrics
    acquisition_cost = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        help_text="Average cost to acquire a new client"
    )
    lifetime_value = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Average lifetime value of clients acquired in this period"
    )
    revenue_per_client = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=0,
        help_text="Average revenue per client in this period"
    )
    total_revenue = models.DecimalField(
        max_digits=15,
        decimal_places=2,
        default=0,
        help_text="Total revenue generated in this period"
    )
    
    # Demographic breakdown (stored as JSON for flexibility)
    age_distribution = models.JSONField(
        default=dict,
        blank=True,
        help_text="Age distribution of new clients (JSON format)"
    )
    gender_distribution = models.JSONField(
        default=dict,
        blank=True,
        help_text="Gender distribution of new clients (JSON format)"
    )
    location_distribution = models.JSONField(
        default=dict,
        blank=True,
        help_text="Geographic distribution of new clients (JSON format)"
    )
    business_type_distribution = models.JSONField(
        default=dict,
        blank=True,
        help_text="Business type distribution of new clients (JSON format)"
    )
    
    # Loan officer performance
    top_performing_officers = models.JSONField(
        default=list,
        blank=True,
        help_text="List of top performing loan officers in this period"
    )
    officer_acquisition_stats = models.JSONField(
        default=dict,
        blank=True,
        help_text="Client acquisition statistics by loan officer"
    )
    
    # Quality metrics
    approval_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=0,
        help_text="Application approval rate as percentage"
    )
    retention_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=0,
        help_text="Client retention rate as percentage"
    )
    satisfaction_score = models.DecimalField(
        max_digits=3,
        decimal_places=1,
        default=0,
        help_text="Average client satisfaction score (0-10)"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    calculated_by = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='calculated_growth_metrics',
        help_text="User who calculated/updated this metric"
    )
    
    class Meta:
        db_table = 'client_growth_metrics'
        unique_together = ['branch', 'period_start', 'period_end', 'period_type']
        ordering = ['-period_end', 'branch__name']
        indexes = [
            models.Index(fields=['branch', 'period_type']),
            models.Index(fields=['period_start', 'period_end']),
            models.Index(fields=['-period_end']),
            models.Index(fields=['period_type']),
        ]
    
    def __str__(self):
        return f"{self.branch.name} - {self.period_type} ({self.period_start} to {self.period_end})"
    
    def get_net_growth(self):
        """Calculate net client growth (new - churned + reactivated)"""
        return self.new_clients - self.churned_clients + self.reactivated_clients
    
    def get_growth_rate(self):
        """Calculate growth rate as percentage"""
        if self.total_clients == 0:
            return 0
        
        net_growth = self.get_net_growth()
        previous_total = self.total_clients - net_growth
        
        if previous_total == 0:
            return 100 if net_growth > 0 else 0
        
        return round((net_growth / previous_total) * 100, 2)
    
    def get_churn_rate(self):
        """Calculate churn rate as percentage"""
        if self.total_clients == 0:
            return 0
        return round((self.churned_clients / self.total_clients) * 100, 2)
    
    def get_acquisition_efficiency(self):
        """Calculate acquisition efficiency (LTV / CAC ratio)"""
        if self.acquisition_cost == 0:
            return 0
        return round(float(self.lifetime_value / self.acquisition_cost), 2)
    
    def update_demographic_data(self, new_clients_queryset):
        """Update demographic distributions based on new clients data"""
        from django.db.models import Count
        
        # Age distribution
        age_ranges = {
            '18-25': 0, '26-35': 0, '36-45': 0, '46-55': 0, '56+': 0
        }
        
        for client in new_clients_queryset:
            if client.date_of_birth:
                from datetime import date
                age = (date.today() - client.date_of_birth).days // 365
                if age <= 25:
                    age_ranges['18-25'] += 1
                elif age <= 35:
                    age_ranges['26-35'] += 1
                elif age <= 45:
                    age_ranges['36-45'] += 1
                elif age <= 55:
                    age_ranges['46-55'] += 1
                else:
                    age_ranges['56+'] += 1
        
        self.age_distribution = age_ranges
        
        # Gender distribution
        gender_dist = new_clients_queryset.values('gender').annotate(
            count=Count('gender')
        )
        self.gender_distribution = {
            item['gender'] or 'Unknown': item['count'] 
            for item in gender_dist
        }
        
        # Location distribution
        location_dist = new_clients_queryset.values('county').annotate(
            count=Count('county')
        )
        self.location_distribution = {
            item['county'] or 'Unknown': item['count'] 
            for item in location_dist
        }
        
        # Business type distribution
        business_dist = new_clients_queryset.values('business_type').annotate(
            count=Count('business_type')
        )
        self.business_type_distribution = {
            item['business_type'] or 'Unknown': item['count'] 
            for item in business_dist
        }
        
        self.save()


class RoleTemplateRollbackPoint(models.Model):
    """
    Rollback points for role template changes with database storage
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    # Rollback identification
    rollback_id = models.CharField(
        max_length=100,
        unique=True,
        help_text="Unique identifier for this rollback point"
    )
    role = models.CharField(
        max_length=20,
        choices=[
            ('admin', 'Administrator'),
            ('team_leader', 'Team Leader'),
            ('loan_officer', 'Loan Officer'),
            ('secretary', 'Secretary'),
            ('auditor', 'Auditor'),
        ],
        help_text="Role this rollback point is for"
    )
    
    # Template data
    template_data = models.JSONField(
        help_text="JSON representation of the role template at this point"
    )
    description = models.TextField(
        blank=True,
        help_text="Description of this rollback point"
    )
    
    # Metadata
    created_by = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_rollback_points',
        help_text="User who created this rollback point"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    
    # Rollback tracking
    is_applied = models.BooleanField(
        default=False,
        help_text="Whether this rollback point has been applied"
    )
    applied_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When this rollback was applied"
    )
    applied_by = models.ForeignKey(
        'users.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='applied_rollback_points',
        help_text="User who applied this rollback"
    )
    
    class Meta:
        db_table = 'role_template_rollback_points'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['role', '-created_at']),
            models.Index(fields=['rollback_id']),
            models.Index(fields=['is_applied']),
            models.Index(fields=['-created_at']),
        ]
    
    def __str__(self):
        status = "Applied" if self.is_applied else "Available"
        return f"{self.role} rollback ({self.created_at.strftime('%Y-%m-%d %H:%M')}) - {status}"
    
    def get_template_summary(self):
        """Get a summary of the template data"""
        try:
            import json
            template = json.loads(self.template_data) if isinstance(self.template_data, str) else self.template_data
            
            total_permissions = 0
            allowed_permissions = 0
            
            for page_name, actions in template.items():
                for action_code, perm_data in actions.items():
                    total_permissions += 1
                    if perm_data.get('is_allowed', False):
                        allowed_permissions += 1
            
            return {
                'total_permissions': total_permissions,
                'allowed_permissions': allowed_permissions,
                'denied_permissions': total_permissions - allowed_permissions,
                'pages_count': len(template.keys())
            }
        except Exception:
            return {
                'total_permissions': 0,
                'allowed_permissions': 0,
                'denied_permissions': 0,
                'pages_count': 0
            }
    
    def mark_as_applied(self, applied_by=None):
        """Mark this rollback point as applied"""
        self.is_applied = True
        self.applied_at = timezone.now()
        self.applied_by = applied_by
        self.save()