
from rest_framework.pagination import PageNumberPagination
from django.db.models import Q,Max
from rest_framework.response import Response
import re
from rest_framework.exceptions import ValidationError
from django.http import JsonResponse
from rest_framework import status
import pytz
from datetime import datetime



OFFENSIVE_WORDS = {
    'porn', 'sex', 'nude', 'adult', 'xxx', 'dick', 'pussy', 'fuck', 'shit', 'asshole',
    'pornhub', 'xvideo', 'xhamster', 'tube8', 'hentai', 'эротика', 'эротика',
    'cunt', 'jizz', 'milf', 'slut', 'whore','choti','banglachoti','bangla','magi','khanki','bancode','bokacoda','madercode','khankirpola',
    'bessa'
}


import random
import string


from django.db.models import Max

def auto_code(digits, model):
    """
    Generates the next sequential numeric code with leading zeros.
    """
    last_code = model.objects.filter(code__regex=r'^\d+$').aggregate(
        max_code=Max('code')
    )['max_code']
    
    if last_code:
        try:
            new_code = int(last_code) + 1
        except ValueError:
            new_code = 1
    else:
        new_code = 1
    
    return f"{new_code:0{digits}d}"



def create_dynamic_pagination(page_size_value):
    """
    Creates and returns a custom pagination class with a dynamic page size.
    """
    class DynamicPageNumberPagination(PageNumberPagination):
        page_size = page_size_value
        page_size_query_param = 'page_size'
        max_page_size = 1000

        def get_paginated_response(self, data):
            return Response({
                'count': self.page.paginator.count,
                'next': self.get_next_link(),
                'previous': self.get_previous_link(),
                'data': data,  
            })

    return DynamicPageNumberPagination




def format_mobile_number(mobile):
    if not mobile:
        return mobile
        
    digits = re.sub(r'\D', '', mobile)
    
    if len(digits) == 11 and digits.startswith('01'):
        return f"{digits[:5]}-{digits[5:]}"
    
    return mobile





def validate_and_format_mobile(value, model_class=None, field_name='mobile', exclude_instance=None):
    """
    Universal mobile number validator and formatter
    """
    if not value:
        return value
    
    formatted_mobile = format_mobile_number(value)
    
    if not re.match(r"^01[3-9]\d{2}-\d{6}$", formatted_mobile):
        raise ValidationError("Enter a valid mobile number in format: 01701-000000")
    
    if model_class:
        queryset = model_class.objects.filter(**{field_name: formatted_mobile})
        
        if exclude_instance and exclude_instance.pk:
            queryset = queryset.exclude(pk=exclude_instance.pk)
        
        if queryset.exists():
            raise ValidationError("Mobile number already exists.")
    
    return formatted_mobile


def validate_and_normalize_email(value, model_class=None, field_name='email', exclude_instance=None):

    if not value:
        return value
    
    normalized_email = value.lower().strip()
    
    if not re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", normalized_email):
        raise ValidationError("Enter a valid email address.")
    
    if model_class:
        queryset = model_class.objects.filter(**{f"{field_name}__iexact": normalized_email})
        
        if exclude_instance and exclude_instance.pk:
            queryset = queryset.exclude(pk=exclude_instance.pk)
        
        if queryset.exists():
            raise ValidationError("User with this email already exists.")
    
    return normalized_email


def validate_name(value, offensive_words_list=None, min_length=2, max_length=100):

    if not value:
        raise ValidationError("Name cannot be empty.")
    
    cleaned_name = value.strip()
    
    if len(cleaned_name) < min_length:
        raise ValidationError(f"Name must be at least {min_length} characters long.")
    
    if len(cleaned_name) > max_length:
        raise ValidationError(f"Name cannot exceed {max_length} characters.")
    
    if not re.match(r"^[a-zA-Z\s.'-]+$", cleaned_name):
        raise ValidationError("Name can only contain alphabets, spaces, dots, apostrophes, and hyphens.")
    
    offensive_words = offensive_words_list or OFFENSIVE_WORDS
    lower_name = cleaned_name.lower()
    
    offensive_found = [word for word in offensive_words if word in lower_name]
    if offensive_found:
        raise ValidationError(f"Name contains inappropriate content: {', '.join(offensive_found)}")
    
    cleaned_name = re.sub(r'\.\.+', '.', cleaned_name)  
    cleaned_name = re.sub(r'--+', '-', cleaned_name)   
    cleaned_name = re.sub(r'\s+', ' ', cleaned_name)   
    cleaned_name = re.sub(r"''+", "'", cleaned_name)    
    
    cleaned_name = re.sub(r'[.\s\-]+$', '', cleaned_name)
    cleaned_name = re.sub(r'^[.\s\-]+', '', cleaned_name)

    if not cleaned_name.strip():
        raise ValidationError("Name cannot be empty after cleaning.")
    
    return cleaned_name


# Part of FaceCreateUpdateSerializer (NEW HELPER METHOD)



# apps/core/utils.py
import pytz
from datetime import datetime
from django.utils import timezone
from rest_framework.exceptions import ValidationError

def tz_now():
    """
    Return current time in Asia/Dhaka timezone formatted as DD-MM-YYYY HH:MM:SS
    """
    dhaka_tz = pytz.timezone("Asia/Dhaka")
    now_in_dhaka = timezone.localtime(timezone.now(), dhaka_tz)
    return now_in_dhaka.strftime("%d %B %Y, %I:%M %p")

def _parse_date(date_str: str, fmt: str = "%d %B %Y, %I:%M %p"):
    """
    Parse date string to date object
    """
    return datetime.strptime(date_str.strip(), fmt).date()

        

def validate_name(value, offensive_words_list=None, min_length=2, max_length=100, field_name="Name"):

    if value is None:
        raise ValidationError(f"{field_name} cannot be null.")
    
    if not isinstance(value, str):
        raise ValidationError(f"{field_name} must be a string.")
    
    cleaned_name = value.strip()
    
    if not cleaned_name:
        raise ValidationError(f"{field_name} cannot be empty.")
    
    # Length validation
    if len(cleaned_name) < min_length:
        raise ValidationError(f"{field_name} must be at least {min_length} characters long.")
    
    if len(cleaned_name) > max_length:
        raise ValidationError(f"{field_name} cannot exceed {max_length} characters.")
    
    # Check for only whitespace
    if not cleaned_name.replace(' ', ''):
        raise ValidationError(f"{field_name} cannot contain only spaces.")
    
    # Offensive words check
    offensive_words = offensive_words_list or OFFENSIVE_WORDS
    if offensive_words:  # Only check if offensive words list is provided
        lower_name = cleaned_name.lower()
        offensive_found = [word for word in offensive_words if word in lower_name]
        
        if offensive_found:
            raise ValidationError(f"{field_name} contains inappropriate content: {', '.join(offensive_found)}")
    
    return cleaned_name



def custom_404_handler(request, exception=None):
    """
    Handles 404 errors and returns a custom JSON response in the required format.
    """
    
    current_time = tz_now()

    response_data = {
        "success": False,
        "error": {
            "code": "4040",
            "details": [
                {
                    "field": "url",
                    "issue": "routing_error",
                    "description": "The requested URL was not found on this server.",
                    "code": "4040"
                }
            ]
        },
        "message": "Page not found.",
        "timestamp": current_time
    }
    
    return JsonResponse(response_data, status=status.HTTP_404_NOT_FOUND)




from decimal import Decimal
import locale

from decimal import Decimal, InvalidOperation

def format_accounting_bdt(number):
    """Return BDT-style formatted number or '-' for empty cells."""
    if number is None or number == '':
        return '-'
    
    try:
        # Convert to Decimal
        if isinstance(number, str):
            number = Decimal(number.replace(',', ''))
        else:
            number = Decimal(str(number))
    except (InvalidOperation, ValueError, TypeError):
        return '-'
    
    # Check if number is zero
    if number == Decimal('0'):
        return '-'
    
    # Format with proper negative handling
    try:
        locale.setlocale(locale.LC_NUMERIC, 'en_IN.utf8')
    except locale.Error:
        pass
    
    # Format absolute value
    absolute_value = abs(number)
    
    # Check if whole number
    if absolute_value == absolute_value.to_integral_value():
        formatted_abs = locale.format_string("%.0f", absolute_value, grouping=True)
    else:
        formatted_abs = locale.format_string("%.2f", absolute_value, grouping=True)
    
    # Add parentheses for negative numbers
    if number < Decimal('0'):
        return f"({formatted_abs})"
    else:
        return formatted_abs



def format_user_email(user_email, school_email, user_role, current_user):

    # Check if inputs are valid
    if not user_email or not school_email:
        return user_email
    
    # Rule 1: If current user is superuser, keep original email
    if current_user and current_user.is_superuser:
        return user_email
    
    # Rule 2: If user role is admin, keep original email
    if user_role == 'admin':
        return user_email
    
    # Rule 3: For non-admin users, use school domain
    try:
        # Extract local part from user_email (before @)
        if '@' in user_email:
            user_local_part = user_email.split('@')[0]
        else:
            user_local_part = user_email
        
        # Extract domain from school_email (after @)
        if '@' in school_email:
            school_domain = school_email.split('@')[1]
        else:
            school_domain = school_email
        
        # Create new email with school domain
        formatted_email = f"{user_local_part}@{school_domain}"
        return formatted_email.lower()  # Convert to lowercase for consistency
    
    except (IndexError, AttributeError):
        # If any error occurs, return original email
        return user_email
