"""
Django Flex Event Tracking

Automatic event logging for all mutations (add, edit, delete).
Configure via DJANGO_FLEX settings to enable audit trails.

Example settings:
    DJANGO_FLEX = {
        'EVENT_TRACKING': {
            'MODEL': 'myapp.models.AuditLog',
            'FIELDS_MAPPING': {
                # internal_key: model_column_name
                'domain': 'module',      # model name -> 'module' column
                'action': 'action',      # action type -> 'action' column
                'user': 'actor',         # user FK -> 'actor_id' column
                'target_type': 'content_type',  # GenericFK type
                'target_id': 'object_id',       # GenericFK id
                'request': 'request_data',      # request JSON
                'response': 'response_data',    # response JSON
                'note': 'note',                 # optional note text
                'company': 'company',           # custom field (e.g., multi-tenant)
            },
            'SCRUB_FIELDS': ['password', 'new_password', 'auth_token'],  # scrubbed from request
        },
    }
"""

import logging

from django_flex.conf import flex_settings

logger = logging.getLogger("django_flex.events")

_event_model_cache = None


def get_event_model():
    """
    Resolve the configured Event model class.

    Returns:
        Model class or None if event tracking is disabled.
    """
    global _event_model_cache

    event_tracking = flex_settings.EVENT_TRACKING
    event_model_path = event_tracking.get("MODEL") if event_tracking else None
    if not event_model_path:
        return None

    # Return cached model if available
    if _event_model_cache is not None:
        return _event_model_cache

    try:
        from django.apps import apps

        # Parse 'app.models.Event' -> app_label='app', model_name='Event'
        parts = event_model_path.rsplit(".", 1)
        if len(parts) != 2:
            logger.error(f"Invalid EVENT_MODEL format: {event_model_path}")
            return None

        module_path, model_name = parts
        # Handle 'app.models.Event' format
        if ".models." in module_path or module_path.endswith(".models"):
            app_label = module_path.split(".")[0]
        else:
            app_label = module_path

        _event_model_cache = apps.get_model(app_label, model_name)
        return _event_model_cache

    except LookupError as e:
        logger.error(f"EVENT_MODEL not found: {e}")
        return None
    except Exception as e:
        logger.warning(f"Error loading EVENT_MODEL: {type(e).__name__}: {e}")
        return None


def create_event(user, model_name, action, target_obj=None, request_data=None, response_data=None, note="", company=None):
    """
    Create an event record for a mutation.

    Uses EVENT_FIELDS dict to map internal keys to model column names:
        - domain: the model name being mutated
        - action: the action type ('create', 'update', 'delete')
        - user: the user who performed the action (FK)
        - target_type: GenericFK content type (optional)
        - target_id: GenericFK object id (optional)
        - request: the request payload (JSON)
        - response: the response payload (JSON)
        - note: optional note text

    Any additional keys in EVENT_FIELDS are copied from target_obj if present,
    or can be passed explicitly (e.g., company for RPC without target).

    Args:
        user: The user who performed the action (or None)
        model_name: The model/module being mutated (domain)
        action: The action performed ('create', 'update', 'delete', or RPC action)
        target_obj: The object that was mutated (optional, for GenericFK)
        request_data: The original request payload (dict)
        response_data: The response payload (dict)
        note: Optional note from request
        company: Optional company (for RPC events without target_obj)

    Returns:
        Event instance or None if tracking disabled or failed
    """
    EventModel = get_event_model()
    if EventModel is None:
        return None

    # Get user's EVENT_TRACKING config
    user_event_tracking = flex_settings.user_settings.get("EVENT_TRACKING", {})

    # Merge with defaults for FIELDS_MAPPING
    from django_flex.conf import DEFAULTS

    default_fields = DEFAULTS["EVENT_TRACKING"]["FIELDS_MAPPING"]
    user_fields = user_event_tracking.get("FIELDS_MAPPING", {})
    event_fields = {**default_fields, **user_fields}  # User overrides defaults

    # Scrub sensitive fields from request data
    scrub_fields = set(user_event_tracking.get("SCRUB_FIELDS", []))
    if scrub_fields and request_data:
        request_data = {k: "***" if k in scrub_fields else v for k, v in request_data.items()}

    try:
        from django.contrib.contenttypes.models import ContentType

        event_data = {}

        # Standard fields - map internal key to column name

        # Domain (model name)
        if "domain" in event_fields:
            event_data[event_fields["domain"]] = model_name

        # Action
        if "action" in event_fields:
            event_data[event_fields["action"]] = action

        # User (FK)
        if "user" in event_fields and user and hasattr(user, "pk") and user.pk:
            column = event_fields["user"]
            event_data[f"{column}_id"] = user.pk

        # Target (GenericFK)
        if "target_type" in event_fields and "target_id" in event_fields:
            if target_obj and hasattr(target_obj, "pk"):
                content_type = ContentType.objects.get_for_model(target_obj)
                event_data[f"{event_fields['target_type']}_id"] = content_type.pk
                event_data[event_fields["target_id"]] = target_obj.pk

        # Request data (JSON)
        if "request" in event_fields and request_data:
            event_data[event_fields["request"]] = request_data

        # Response data (JSON)
        if "response" in event_fields and response_data:
            event_data[event_fields["response"]] = response_data

        # Note
        if "note" in event_fields and note:
            event_data[event_fields["note"]] = note

        # Custom fields - copy from target_obj if present, or use explicit params
        # This handles things like 'company', 'tenant', etc.
        standard_keys = {"domain", "action", "user", "target_type", "target_id", "request", "response", "note"}
        for internal_key, column_name in event_fields.items():
            if internal_key not in standard_keys:
                value = None
                # 1. Try explicit kwargs (e.g., company=company passed to function)
                if internal_key == "company" and company is not None:
                    if hasattr(company, "pk"):
                        event_data[f"{column_name}_id"] = company.pk
                    else:
                        event_data[f"{column_name}_id"] = company
                    continue
                # 2. Try to get from target_obj
                if target_obj:
                    # Try both field_id (for FK) and field
                    fk_attr = f"{internal_key}_id"
                    if hasattr(target_obj, fk_attr):
                        value = getattr(target_obj, fk_attr, None)
                        if value is not None:
                            event_data[f"{column_name}_id"] = value
                    elif hasattr(target_obj, internal_key):
                        value = getattr(target_obj, internal_key, None)
                        if value is not None:
                            event_data[column_name] = value

        return EventModel.objects.create(**event_data)

    except Exception as e:
        logger.warning(f"Failed to create event: {type(e).__name__}: {e}")
        return None


def clear_event_model_cache():
    """Clear the cached event model (useful for testing)."""
    global _event_model_cache
    _event_model_cache = None
