import logging

from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from django.utils.module_loading import import_string

from invoicing import settings as invoicing_settings

logger = logging.getLogger(__name__)


class InvoiceManagerMixin(object):
    required_origin = None

    def __init__(self, *args, **kwargs):
        """
        Initialize manager and optionally override exporter configuration
        from INVOICING_MANAGERS settings.

        Supported per-manager options (keyed by the manager's dotted path):
        - exporter_class: dotted path to the main exporter class
        - exporter_subclasses: list of dotted paths to exporter subclasses

        When not provided, the concrete manager's class-level attributes are kept as defaults.
        """
        super().__init__(*args, **kwargs)

        config = self.manager_settings

        exporter_path = config.get('exporter_class')
        if exporter_path:
            self.exporter_class = import_string(exporter_path)

        exporter_subclass_paths = config.get('exporter_subclasses')
        if exporter_subclass_paths:
            self.exporter_subclasses = [
                import_string(path) for path in exporter_subclass_paths
            ]

    @property
    def manager_settings(self):
        """Get the settings for this manager from INVOICING_MANAGERS config."""
        key = f"{self.__module__}.{self.__class__.__name__}"
        return invoicing_settings.INVOICING_MANAGERS.get(key, {})

    def _is_export_qs_valid(self, request, exporter):
        """
        Validate queryset to be exported.

        - Ensures there is something to export.
        - When required_origin is set: ensures all exported objects share that single origin.
        """
        queryset = exporter.get_queryset()

        # 1) Check if there is anything to export
        if queryset is None or not queryset.exists():
            messages.warning(request, _("%s: There is no invoice selected to export." % exporter.__class__.__name__))
            return False

        # 2) When required_origin is set: enforce single origin and that it matches.
        #    (Otherwise allow multiple origins in the queryset.)
        if self.required_origin is not None:
            # Single DB hit for distinct origins. order_by() clears default ordering
            # so distinct() applies only to origin (otherwise we get one row per invoice).
            origins = list(queryset.values_list("origin", flat=True).order_by().distinct())
            if len(origins) != 1:
                messages.warning(request, _("%s: All exported invoices must have the same origin." % exporter.__class__.__name__))
                return False
            if origins[0] != self.required_origin:
                messages.warning(request, _("%s: All exported invoices must have the expected origin." % exporter.__class__.__name__))
                return False

        return True

    def _execute_export(self, request, exporter_class, exporter_params, queryset):
        """
        Common export logic for email-based exports.

        Args:
            request: The HTTP request object
            exporter_class: The exporter class to use
            exporter_params: The params of the exporter
            queryset: The queryset of invoices to export
        """
        if exporter_params is None:
            exporter_params = {"user": request.user, "recipients": [request.user], "params": {}}

        if queryset is not None and queryset.exists():
            exporter_params = {**exporter_params, 'queryset': queryset}

        exporter = exporter_class(**exporter_params)

        if not self._is_export_qs_valid(request, exporter):
            return

        qs_count = exporter.get_queryset().count()
        logger.info(
            f"User {request.user} (ID: {request.user.id}) executing export with {qs_count} invoice(s)",
            extra={
                'user_id': request.user.id,
                'exporter_class': exporter_class,
                'exporter_params': exporter_params
            }
        )

        from outputs.usecases import execute_export
        from django.utils import translation
        execute_export(exporter, language=translation.get_language())
        messages.info(request, _('Export of %d invoice(s) queued and will be sent to email') % qs_count)
