import os
from datetime import datetime
from io import BytesIO
from PIL import Image as PILImage
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.db import models
from django.utils import timezone
from django.utils.text import slugify


from django.contrib.auth.base_user import BaseUserManager

from django.contrib.auth.base_user import BaseUserManager

class CustomUserManager(BaseUserManager):
    def create_user(self, email=None, password=None, **extra_fields):
        if email:
            email = self.normalize_email(email)
            extra_fields['email'] = email

        user = self.model(**extra_fields)
        if password:
            user.set_password(password)
        else:
            user.set_unusable_password()

        user.save(using=self._db)
        return user

    def create_superuser(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)

        return self.create_user(email=email, password=password, **extra_fields)



def user_image_upload_to(instance, filename):
    """
    Returns a safe upload path for user profile images.
    Instead of using the original filename (which might contain non-ASCII characters),
    we generate a new filename using the current datetime and a slugified username.
    The file extension is preserved.
    """
    ext = os.path.splitext(filename)[1]  # e.g. ".png" or ".jpg"
    safe_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{slugify(instance.username)}{ext}"
    return os.path.join('images', 'users', safe_filename)


def work_license_upload_to(instance, filename):
    """
    Returns a safe upload path for the user's work license copy.
    """
    ext = os.path.splitext(filename)[1]
    safe_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{slugify(instance.username)}{ext}"
    return os.path.join('work_licenses', safe_filename)


def tazkira_upload_to(instance, filename):
    """
    Returns a safe upload path for the user's tazkira copy.
    """
    ext = os.path.splitext(filename)[1]
    safe_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{slugify(instance.username)}{ext}"
    return os.path.join('tazkira', safe_filename)


class Gender(models.IntegerChoices):
    MALE = 1, 'Male'
    FEMALE = 2, 'Female'


class User(AbstractUser):
    SYSTEM_ROLE_CHOICES = [
        ("HEAD_USER", "Head User"),
        ("BRANCH_MANAGER", "Branch Manager"),
        ("ACCOUNTANT", "Accountant"),
        ("CASHIER", "Cashier"),
        ("SALES_USER", "Sales User"),
        ("PURCHASE_USER", "Purchase User"),
        ("WAREHOUSE_USER", "Warehouse User"),
        ("REPORT_VIEWER", "Report Viewer"),
    ]
    username = models.CharField(max_length=150, unique=True, blank=True, null=True)
    is_seller = models.BooleanField(default=False)
    tazkira_number = models.CharField(max_length=200, blank=True, null=True)
    phone_number = models.CharField(max_length=200, blank=True, null=True, unique=True)
    address = models.CharField(max_length=200, blank=True, null=True)
    email = models.EmailField(blank=True, null=True)
    work_license_copy = models.FileField(
        upload_to=work_license_upload_to,
        blank=True,
        null=True
    )
    verified = models.BooleanField(default=False, null=True, blank=True)
    # Use our custom callable to ensure a safe, ASCII-only filename is generated.
    image = models.ImageField(null=True, blank=True, upload_to=user_image_upload_to)
    is_visible_phone = models.BooleanField(default=True)
    shop_address = models.CharField(max_length=200, blank=True, null=True)
    tazkira_copy = models.FileField(
        upload_to=tazkira_upload_to,
        blank=True,
        null=True
    )
    shop_name = models.CharField(max_length=200, blank=True, null=True)
    gender = models.IntegerField(choices=Gender.choices, default=1)
    is_mobile_verified = models.BooleanField(default=False)
    mobile_otp = models.CharField(max_length=6, null=True, blank=True)
    temporary_verification_expiration = models.DateField(null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    # New fields for email OTP verification
    is_email_verified = models.BooleanField(default=False)
    otp_secret = models.CharField(max_length=16,null=True, blank=True)
    otp_expiry = models.DateTimeField(null=True, blank=True)
    
    # New fields for tracking creation and modification times
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    # Optional location fields
    longitude = models.FloatField(blank=True, null=True)
    latitude = models.FloatField(blank=True, null=True)

    # Tab-based permissions for staff (non-superuser). List of tab keys the user can access.
    # Super admin (is_superuser) has full access regardless. Empty list = no tab access.
    allowed_tabs = models.JSONField(
        default=list,
        blank=True,
        help_text="List of tab keys this user can access. Ignored if is_superuser.",
    )
    system_role = models.CharField(max_length=32, choices=SYSTEM_ROLE_CHOICES, blank=True, default="")
    action_permissions = models.JSONField(default=list, blank=True, editable=False)

    objects = CustomUserManager()
    
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = []
    
    def __str__(self):
        return self.username if self.username else f"{self.first_name} {self.last_name}"

    def save(self, *args, **kwargs):
        # Handle temporary verification expiration logic.
        if (self.temporary_verification_expiration and 
                self.temporary_verification_expiration <= timezone.now().date()):
            self.verified = False
            self.temporary_verification_expiration = None  # Reset if expired

        # First, call the original save so that any uploaded files become available.
        super().save(*args, **kwargs)

        # Process the profile image if provided.
        if self.image:
            try:
                # Open the image using Pillow.
                self.image.open()
                img = PILImage.open(self.image)
                # Convert to RGB to handle images with an alpha channel.
                img = img.convert("RGB")
                # Resize the image (maximum dimensions 800x800, preserving aspect ratio).
                img.thumbnail((800, 800), PILImage.Resampling.LANCZOS)

                # Generate a new safe filename for the processed image (with .webp extension).
                safe_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{slugify(self.username)}.webp"

                # Save the processed image to an in-memory buffer in WebP format.
                buffer = BytesIO()
                img.save(buffer, format="webp", quality=80)
                buffer.seek(0)
                webp_content = ContentFile(buffer.getvalue())

                # Optionally delete the original image if its extension is not already .webp.
                _, ext = os.path.splitext(self.image.name)
                if ext.lower() != ".webp":
                    try:
                        default_storage.delete(self.image.name)
                    except Exception:
                        pass

                # Update the image field with the new file using our safe filename.
                self.image.save(safe_filename, webp_content, save=False)
            except Exception as e:
                # Log or handle errors as needed.
                print(f"Error processing profile image for user '{self.username}': {e}")

        # Save the instance again with any updated file fields.
        super().save(update_fields=['image'])



class UserAuditLog(models.Model):
    ACTION_CHOICES = [
        ('update', 'Update'),
        ('delete', 'Delete'),
        ('LOGIN', 'Login'),
        ('CREATE', 'Create'),
        ('UPDATE', 'Update'),
        ('POST', 'Post'),
        ('VOID', 'Void'),
        ('PAYMENT', 'Payment'),
        ('RETURN', 'Return'),
        ('TRANSFER', 'Transfer'),
        ('ADJUSTMENT', 'Stock Adjustment'),
        ('MANUAL_BATCH_OVERRIDE', 'Manual Batch Override'),
        ('PERMISSION_CHANGE', 'Permission Change'),
        ('EXCHANGE_RATE_CHANGE', 'Exchange Rate Change'),
    ]

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='audit_logs'
    )
    changed_fields = models.JSONField(
        help_text="Stores a dictionary of changed fields with old and new values."
    )
    timestamp = models.DateTimeField(auto_now_add=True)
    changed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='changed_audits',
        help_text="The user who made the change. If the user updates their own profile, this will be the same as `user`."
    )
    action = models.CharField(
        max_length=40,
        choices=ACTION_CHOICES,
        default='update',
        help_text="Type of action performed."
    )
    model_name = models.CharField(max_length=100, blank=True)
    object_id = models.CharField(max_length=100, blank=True)
    reference = models.CharField(max_length=255, blank=True)
    branch_id = models.PositiveBigIntegerField(null=True, blank=True, db_index=True)
    before_data = models.JSONField(default=dict, blank=True)
    after_data = models.JSONField(default=dict, blank=True)
    ip_address = models.GenericIPAddressField(null=True, blank=True)

    def __str__(self):
        return f"AuditLog for {self.user.username} at {self.timestamp} ({self.get_action_display()})"
