import uuid
import hashlib
from typing import Optional, Dict, Any
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
from django.contrib.auth import get_user_model
from celery import shared_task
import logging

logger = logging.getLogger(__name__)
User = get_user_model()


def generate_unique_id() -> str:
    """
    Generate a unique identifier.
    """
    return str(uuid.uuid4())


def generate_hash(data: str) -> str:
    """
    Generate a SHA-256 hash of the given data.
    """
    return hashlib.sha256(data.encode()).hexdigest()


def send_email_notification(
    subject: str,
    template_name: str,
    context: Dict[str, Any],
    recipient_list: list,
    from_email: Optional[str] = None
) -> bool:
    """
    Send an email notification using a template.
    """
    try:
        html_message = render_to_string(template_name, context)
        plain_message = strip_tags(html_message)
        
        send_mail(
            subject=subject,
            message=plain_message,
            from_email=from_email or settings.DEFAULT_FROM_EMAIL,
            recipient_list=recipient_list,
            html_message=html_message,
            fail_silently=False,
        )
        return True
    except Exception as e:
        logger.error(f"Failed to send email: {e}")
        return False


@shared_task(bind=True, max_retries=3)
def send_async_email(
    self,
    subject: str,
    template_name: str,
    context: Dict[str, Any],
    recipient_list: list,
    from_email: Optional[str] = None
):
    """
    Celery task to send email asynchronously.
    """
    try:
        success = send_email_notification(
            subject=subject,
            template_name=template_name,
            context=context,
            recipient_list=recipient_list,
            from_email=from_email
        )
        if not success:
            raise Exception("Failed to send email")
        return "Email sent successfully"
    except Exception as exc:
        logger.error(f"Email sending failed: {exc}")
        if self.request.retries < self.max_retries:
            raise self.retry(countdown=60 * (2 ** self.request.retries))
        raise exc


def validate_email_domain(email: str, allowed_domains: Optional[list] = None) -> bool:
    """
    Validate if email domain is allowed.
    """
    if not allowed_domains:
        return True
    
    domain = email.split('@')[-1].lower()
    return domain in [d.lower() for d in allowed_domains]


def get_client_ip(request) -> str:
    """
    Get the client IP address from the request.
    """
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip


def format_file_size(size_bytes: int) -> str:
    """
    Format file size in human readable format.
    """
    if size_bytes == 0:
        return "0B"
    
    size_names = ["B", "KB", "MB", "GB", "TB"]
    i = 0
    while size_bytes >= 1024 and i < len(size_names) - 1:
        size_bytes /= 1024.0
        i += 1
    
    return f"{size_bytes:.1f}{size_names[i]}"


def sanitize_filename(filename: str) -> str:
    """
    Sanitize filename by removing/replacing invalid characters.
    """
    import re
    # Remove invalid characters
    filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
    # Remove leading/trailing spaces and dots
    filename = filename.strip(' .')
    # Limit length
    if len(filename) > 255:
        name, ext = filename.rsplit('.', 1) if '.' in filename else (filename, '')
        filename = name[:255-len(ext)-1] + '.' + ext if ext else name[:255]
    
    return filename