from django.contrib.auth.models import AbstractUser
from django.db import models
from django.core.validators import RegexValidator
from django.utils import timezone
import uuid


class CustomUser(AbstractUser):
    """
    Custom user model with additional fields
    """
    # Override the id field to use UUID
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    ROLE_CHOICES = [
        ('admin', 'Admin'),
        ('team_leader', 'Team Leader'),
        ('loan_officer', 'Loan Officer'),
        ('secretary', 'Secretary'),
        ('borrower', 'Borrower'),
    ]
    
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('inactive', 'Inactive'),
        ('suspended', 'Suspended'),
        ('blacklisted', 'Blacklisted'),
    ]
    
    MARITAL_STATUS_CHOICES = [
        ('single', 'Single'),
        ('married', 'Married'),
        ('divorced', 'Divorced'),
        ('widowed', 'Widowed'),
    ]

    # Override email to make it nullable
    email = models.EmailField(unique=True, null=True, blank=True)

    # Basic Info
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='borrower')
    phone_regex = RegexValidator(
        regex=r'^\+?254?\d{9,15}$',
        message="Phone number must be entered in the format: '+254XXXXXXXXX'"
    )
    phone_number = models.CharField(validators=[phone_regex], max_length=17, unique=True)
    id_number = models.CharField(max_length=20, unique=True, null=True, blank=True)  # Made nullable for existing records
    date_of_birth = models.DateField(null=True, blank=True)
    gender = models.CharField(max_length=10, choices=[('M', 'Male'), ('F', 'Female'), ('O', 'Other')], null=True, blank=True)
    marital_status = models.CharField(max_length=20, choices=MARITAL_STATUS_CHOICES, null=True, blank=True)
    nationality = models.CharField(max_length=50, null=True, blank=True)
    
    # Additional Personal Info
    nickname = models.CharField(max_length=100, blank=True, null=True)
    place_of_birth = models.CharField(max_length=200, blank=True, null=True)
    domicile = models.CharField(max_length=200, blank=True, null=True)
    cp_domicile = models.CharField(max_length=200, blank=True, null=True)
    start_time = models.TimeField(blank=True, null=True)
    registration_date = models.DateField(blank=True, null=True)
    
    # Address
    physical_address = models.TextField(blank=True, null=True)
    postal_address = models.CharField(max_length=100, blank=True, null=True)
    postal_code = models.CharField(max_length=20, blank=True, null=True)
    postal_code_business = models.CharField(max_length=20, blank=True, null=True)
    city = models.CharField(max_length=100, blank=True, null=True)
    county = models.CharField(max_length=100, blank=True, null=True)
    country = models.CharField(max_length=100, default='Kenya')
    physical_location = models.CharField(max_length=200, blank=True, null=True)
    
    # Business Info
    business_name = models.CharField(max_length=200, blank=True, null=True)
    business_type = models.CharField(max_length=100, blank=True, null=True)
    other_business_type = models.CharField(max_length=200, blank=True, null=True)
    business_address = models.TextField(blank=True, null=True)
    business_registration_number = models.CharField(max_length=50, blank=True, null=True)
    
    # Financial Information
    monthly_income = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True, help_text="Monthly income in KES")
    employer = models.CharField(max_length=200, blank=True, null=True)
    capital_invested = models.DecimalField(max_digits=15, decimal_places=2, null=True, blank=True, help_text="Capital invested in KES")
    source_of_funds = models.TextField(blank=True, null=True)
    expected_turnover = models.DecimalField(max_digits=15, decimal_places=2, null=True, blank=True, help_text="Expected turnover in KES")
    
    # Documents
    id_document = models.FileField(upload_to='kyc/id_documents/', null=True, blank=True)
    selfie = models.FileField(upload_to='kyc/selfies/', null=True, blank=True)
    utility_bill = models.FileField(upload_to='kyc/utility_bills/', null=True, blank=True)
    bank_statement = models.FileField(upload_to='kyc/bank_statements/', null=True, blank=True)
    business_license = models.FileField(upload_to='kyc/business_licenses/', null=True, blank=True)
    tax_certificate = models.FileField(upload_to='kyc/tax_certificates/', null=True, blank=True)
    logbook = models.FileField(upload_to='kyc/logbooks/', null=True, blank=True)
    title_deed = models.FileField(upload_to='kyc/title_deeds/', null=True, blank=True)
    signature = models.FileField(upload_to='kyc/signatures/', null=True, blank=True)
    other_documents = models.JSONField(default=list, blank=True, help_text="List of additional document URLs and descriptions")
    
    # Declaration
    declaration_name = models.CharField(max_length=200, blank=True, null=True)
    personal_pin = models.CharField(max_length=50, blank=True, null=True)
    
    # Recommenders
    recommender_name = models.CharField(max_length=200, blank=True, null=True)
    recommender_id = models.CharField(max_length=50, blank=True, null=True)
    recommender_tel = models.CharField(max_length=20, blank=True, null=True)
    recommender_mobile = models.CharField(max_length=20, blank=True, null=True)
    recommender_residence = models.CharField(max_length=200, blank=True, null=True)
    
    # Guarantors
    guarantor_name = models.CharField(max_length=200, blank=True, null=True)
    guarantor_id = models.CharField(max_length=50, blank=True, null=True)
    guarantor_tel = models.CharField(max_length=20, blank=True, null=True)
    guarantor_mobile = models.CharField(max_length=20, blank=True, null=True)
    guarantor_residence = models.CharField(max_length=200, blank=True, null=True)
    
    # Status
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
    is_verified = models.BooleanField(default=False)
    is_phone_verified = models.BooleanField(default=False)
    is_email_verified = models.BooleanField(default=False)
    verification_date = models.DateTimeField(null=True, blank=True)
    verified_by = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='verified_users')
    
    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'users'
        ordering = ['-date_joined']
        
    def __str__(self):
        return f"{self.get_full_name()} ({self.email or self.phone_number})"
    
    def get_full_name(self):
        """Return full name or username if no name set"""
        full_name = super().get_full_name()
        return full_name if full_name else self.username
    
    def save(self, *args, **kwargs):
        """Override save to handle verification status"""
        if self.verified_by and not self.verification_date:
            self.verification_date = timezone.now()
            self.is_verified = True
        super().save(*args, **kwargs)
    
    def add_document(self, url, description, document_type):
        """Add a new document to other_documents"""
        if not self.other_documents:
            self.other_documents = []
        
        self.other_documents.append({
            'url': url,
            'description': description,
            'type': document_type,
            'uploaded_at': timezone.now().isoformat()
        })
        self.save()
    
    def get_short_name(self):
        return self.first_name or self.username
    
    def is_admin(self):
        return self.role == 'admin'
    
    def is_loan_officer(self):
        return self.role == 'loan_officer'
    
    def is_auditor(self):
        return self.role == 'auditor'
    
    def is_borrower(self):
        return self.role == 'borrower'
    
    def is_team_leader(self):
        return self.role == 'team_leader'
    
    def is_secretary(self):
        return self.role == 'secretary'
    
    def is_active_user(self):
        return self.status == 'active'
    
    def has_permission(self, module, action):
        """Check if user has permission for a specific module and action"""
        if self.role == 'admin':
            return True  # Admin has all permissions
        
        try:
            from .models import RolePermission
            permission = RolePermission.objects.get(
                role=self.role,
                module=module,
                action=action
            )
            return permission.is_allowed
        except (RolePermission.DoesNotExist, ImportError):
            return False
    
    def get_permissions(self):
        """Get all permissions for the user's role"""
        if self.role == 'admin':
            # Return all possible permissions for admin
            permissions = {}
            try:
                from .models import RolePermission
                for module in RolePermission.MODULE_CHOICES:
                    permissions[module[0]] = {}
                    for action in RolePermission.ACTION_CHOICES:
                        permissions[module[0]][action[0]] = True
            except ImportError:
                # Fallback if RolePermission is not available
                pass
            return permissions
        
        try:
            from .models import RolePermission
            role_permissions = RolePermission.objects.filter(role=self.role)
            permissions = {}
            for perm in role_permissions:
                if perm.module not in permissions:
                    permissions[perm.module] = {}
                permissions[perm.module][perm.action] = perm.is_allowed
            return permissions
        except ImportError:
            return {}
    
    def log_access(self, action, module, object_type=None, object_id=None, description="", request=None):
        """Log user access for audit trail"""
        try:
            from .models import UserAccessLog
            UserAccessLog.objects.create(
                user=self,
                action=action,
                module=module,
                object_type=object_type,
                object_id=object_id,
                description=description,
                ip_address=request.META.get('REMOTE_ADDR') 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 request.session else None
            )
        except ImportError:
            # Fallback if UserAccessLog is not available
            pass
    
    def update_last_login(self):
        self.last_login_at = timezone.now()
        self.save(update_fields=['last_login_at'])


class RolePermission(models.Model):
    """
    Role-based permissions for different user roles
    """
    ROLE_CHOICES = [
        ('admin', 'Admin'),
        ('team_leader', 'Team Leader'),
        ('loan_officer', 'Loan Officer'),
        ('secretary', 'Secretary'),
        ('borrower', 'Borrower'),
    ]
    
    MODULE_CHOICES = [
        ('users', 'User Management'),
        ('loans', 'Loan Management'),
        ('reports', 'Reports & Analytics'),
        ('documents', 'Document Management'),
        ('settings', 'System Settings'),
        ('notifications', 'Notifications'),
        ('audit', 'Audit Logs'),
        ('kyc', 'KYC Management'),
        ('payments', 'Payment Management'),
        ('communications', 'Communications'),
    ]
    
    ACTION_CHOICES = [
        ('view', 'View'),
        ('create', 'Create'),
        ('edit', 'Edit'),
        ('delete', 'Delete'),
        ('approve', 'Approve'),
        ('reject', 'Reject'),
        ('export', 'Export'),
        ('import', 'Import'),
        ('manage', 'Manage'),
    ]
    
    role = models.CharField(max_length=20, choices=ROLE_CHOICES)
    module = models.CharField(max_length=20, choices=MODULE_CHOICES)
    action = models.CharField(max_length=20, choices=ACTION_CHOICES)
    is_allowed = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        unique_together = ['role', 'module', 'action']
        db_table = 'role_permissions'
        ordering = ['role', 'module', 'action']
    
    def __str__(self):
        return f"{self.role} - {self.module} - {self.action} ({'Allowed' if self.is_allowed else 'Denied'})"


class UserAccessLog(models.Model):
    """
    Detailed access logs for user activities
    """
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='access_logs')
    action = models.CharField(max_length=50)
    module = models.CharField(max_length=50)
    object_type = models.CharField(max_length=50, blank=True, null=True)
    object_id = models.CharField(max_length=50, blank=True, null=True)
    description = models.TextField()
    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)
    accessed_at = models.DateTimeField(auto_now_add=True)
    response_time = models.FloatField(blank=True, null=True)  # in milliseconds
    status_code = models.IntegerField(blank=True, null=True)
    
    class Meta:
        db_table = 'user_access_logs'
        ordering = ['-accessed_at']
        indexes = [
            models.Index(fields=['user', 'accessed_at']),
            models.Index(fields=['action', 'accessed_at']),
            models.Index(fields=['module', 'accessed_at']),
        ]
    
    def __str__(self):
        return f"{self.user.get_full_name()} - {self.action} - {self.accessed_at}"


class OTPVerification(models.Model):
    """
    OTP verification model for phone/email verification
    """
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    otp_code = models.CharField(max_length=6)
    is_used = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()
    
    class Meta:
        db_table = 'otp_verifications'
    
    def __str__(self):
        return f"OTP for {self.user.phone_number} - {self.otp_code}"
    
    def is_expired(self):
        return timezone.now() > self.expires_at
