"""
Audit Log Service — سرویس مرکزی ثبت تغییرات.
"""
import logging

from django.db.models import Model

from .models import AuditAction, AuditLog

logger = logging.getLogger('apps')


class AuditLogService:
    """سرویس ثبت audit log."""

    @classmethod
    def log(
        cls,
        action: str,
        resource_type: str,
        resource_id: str,
        user=None,
        changes: dict | None = None,
        module: str = '',
        resource_repr: str = '',
        ip_address: str | None = None,
        user_agent: str = '',
        metadata: dict | None = None,
        tenant_schema: str = '',
    ) -> AuditLog:
        """ثبت یک entry در audit log."""
        entry = AuditLog(
            action=action,
            resource_type=resource_type,
            resource_id=str(resource_id),
            resource_repr=resource_repr,
            module=module,
            changes=changes or {},
            ip_address=ip_address,
            user_agent=user_agent,
            metadata=metadata or {},
            tenant_schema=tenant_schema,
        )

        if user and hasattr(user, 'pk') and user.pk:
            entry.user = user
            entry.user_email = getattr(user, 'email', '')
            entry.user_display_name = getattr(user, 'full_name', '') or str(user)

        try:
            entry.save()
        except Exception as e:
            logger.error(f"Failed to save audit log: {e}", exc_info=True)

        return entry

    @classmethod
    def log_model_change(
        cls,
        instance: Model,
        action: str,
        changes: dict | None = None,
        user=None,
        module: str = '',
        request=None,
    ) -> AuditLog:
        """ثبت تغییر مدل Django."""
        resource_type = instance.__class__.__name__
        resource_id = str(instance.pk) if instance.pk else ''
        resource_repr = str(instance)[:500]

        ip_address = None
        user_agent = ''
        tenant_schema = ''

        if request:
            ip_address = cls._get_client_ip(request)
            user_agent = request.META.get('HTTP_USER_AGENT', '')[:500]
            tenant_schema = getattr(request, 'tenant', None)
            if tenant_schema:
                tenant_schema = getattr(tenant_schema, 'schema_name', '')

        if not module:
            module = cls._guess_module(instance)

        return cls.log(
            action=action,
            resource_type=resource_type,
            resource_id=resource_id,
            resource_repr=resource_repr,
            user=user,
            changes=changes,
            module=module,
            ip_address=ip_address,
            user_agent=user_agent,
            tenant_schema=tenant_schema,
        )

    @classmethod
    def get_resource_history(cls, resource_type: str, resource_id: str):
        """تاریخچه تغییرات یک موجودیت."""
        return AuditLog.objects.filter(
            resource_type=resource_type,
            resource_id=str(resource_id),
        ).order_by('-created_at')

    @classmethod
    def log_access_denied(
        cls,
        user=None,
        permission: str = '',
        resource_type: str = '',
        resource_id: str = '',
        module: str = '',
        request=None,
        metadata: dict | None = None,
    ) -> AuditLog:
        """ثبت دسترسی رد شده در audit log."""
        ip_address = None
        user_agent = ''
        tenant_schema = ''

        if request:
            ip_address = cls._get_client_ip(request)
            user_agent = request.META.get('HTTP_USER_AGENT', '')[:500]
            tenant_schema = getattr(request, 'tenant', None)
            if tenant_schema:
                tenant_schema = getattr(tenant_schema, 'schema_name', '')

        return cls.log(
            action=AuditAction.ACCESS_DENIED,
            resource_type=resource_type or 'unknown',
            resource_id=resource_id or '',
            resource_repr=f'Permission: {permission}',
            user=user,
            module=module,
            ip_address=ip_address,
            user_agent=user_agent,
            metadata={
                'permission_required': permission,
                **(metadata or {}),
            },
            tenant_schema=tenant_schema,
        )

    @classmethod
    def log_permission_change(
        cls,
        user=None,
        target_user=None,
        action_detail: str = '',
        changes: dict | None = None,
        request=None,
    ) -> AuditLog:
        """ثبت تغییر در دسترسی/نقش یک کاربر."""
        ip_address = None
        user_agent = ''
        tenant_schema = ''

        if request:
            ip_address = cls._get_client_ip(request)
            user_agent = request.META.get('HTTP_USER_AGENT', '')[:500]
            tenant_schema = getattr(request, 'tenant', None)
            if tenant_schema:
                tenant_schema = getattr(tenant_schema, 'schema_name', '')

        return cls.log(
            action=AuditAction.PERMISSION_CHANGE,
            resource_type='User',
            resource_id=str(target_user.pk) if target_user else '',
            resource_repr=action_detail,
            user=user,
            changes=changes or {},
            module='permission',
            ip_address=ip_address,
            user_agent=user_agent,
            tenant_schema=tenant_schema,
        )

    @classmethod
    def cleanup_old_logs(cls, days: int = 90) -> int:
        """حذف لاگ‌های قدیمی‌تر از تعداد روز مشخص شده."""
        from django.utils import timezone
        from datetime import timedelta

        cutoff = timezone.now() - timedelta(days=days)
        deleted, _ = AuditLog.objects.filter(created_at__lt=cutoff).delete()
        logger.info(f"Cleaned up {deleted} audit log entries older than {days} days")
        return deleted

    @classmethod
    def compute_changes(cls, old_data: dict, new_data: dict) -> dict:
        """محاسبه diff بین دو dict."""
        changes = {}
        all_keys = set(old_data.keys()) | set(new_data.keys())

        for key in all_keys:
            old_val = old_data.get(key)
            new_val = new_data.get(key)
            if old_val != new_val:
                changes[key] = {
                    'old': cls._serialize_value(old_val),
                    'new': cls._serialize_value(new_val),
                }

        return changes

    @classmethod
    def _serialize_value(cls, val):
        """تبدیل مقدار به JSON-serializable."""
        if val is None:
            return None
        if isinstance(val, (str, int, float, bool)):
            return val
        return str(val)

    @classmethod
    def _get_client_ip(cls, request) -> str | None:
        """دریافت IP واقعی از request."""
        x_forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded:
            return x_forwarded.split(',')[0].strip()
        return request.META.get('REMOTE_ADDR')

    @classmethod
    def _guess_module(cls, instance: Model) -> str:
        """حدس ماژول از app_label مدل."""
        meta = getattr(instance, '_meta', None)
        if meta:
            return meta.app_label
        return ''
