import logging
from typing import Any

from django.db.models import F, QuerySet
from django.views.generic.list import BaseListView
from django_select2.views import AutoResponseView
from .mixins import (
    TenantView,
)

from console_base.forms import Select2FilterForm
from console_base.http import FastJsonResponse
from console_base.permissions import LCLoginRequiredMixin
from console_base.typehints import LCHttpRequest, SELECT_OBJECTS_TYPE
from console_base.utils import get_model_fields

logger = logging.getLogger(__name__)


# ----------------------------------------------------------------------
class LCAutoResponseChoicesView(
    LCLoginRequiredMixin,
    AutoResponseView,
):
    """
    JSON View for django_select2 mixins with ChoiceField
    rather than ModelChoiceField.
    """

    def get_object_list(self, request: LCHttpRequest) -> SELECT_OBJECTS_TYPE:
        raise NotImplementedError

    def get(self, request: LCHttpRequest, *args: Any, **kwargs: Any) -> FastJsonResponse:
        """
        Return a :class:`console_base.http.FastJsonResponse`.

        Example::

            {
                'results': [
                    {
                        'text': "foo",
                        'id': 123
                    }
                ],
                'more': true
            }

        """
        self.get_object_list(request=request)

        self.term = kwargs.get('term', request.GET.get('term', '')).lower()
        return FastJsonResponse({
            'results': [
                {
                    'text': option[1],
                    'id': option[0],
                }
                for option in self.object_list
                if self.term in option[1].lower()
            ],
            'more': False,
        })


# ----------------------------------------------------------------------
class LCAutoResponseView(
    LCLoginRequiredMixin,
    TenantView,
    AutoResponseView,
):
    """JSON View for django_select2 mixins"""

    def get(self, request: LCHttpRequest, *args: Any, **kwargs: Any) -> FastJsonResponse:
        """
        Return a :class:`pantry.http.FastJsonResponse`.

        Example::

            {
                'results': [
                    {
                        'text': "foo",
                        'id': 123
                    }
                ],
                'more': true
            }

        """
        self.widget = self.get_widget_or_404()
        self.term = kwargs.get('term', request.GET.get('term', ''))
        self.object_list = self.get_queryset()
        context = self.get_context_data()
        return FastJsonResponse({
            'results': [
                {
                    'text': self.widget.label_from_instance(obj),
                    'id': obj.pk,
                }
                for obj in context['object_list']
            ],
            'more': context['page_obj'].has_next(),
        })

    def get_queryset(self) -> QuerySet:
        qs = super().get_queryset()
        fields = get_model_fields(qs.model)  # type: ignore[arg-type]
        if 'name' in fields:
            return qs.order_by('name')
        if 'model' in fields:
            return qs.order_by('model')
        return qs.order_by('pk')


# ----------------------------------------------------------------------
class LCSelect2ListView(  # type: ignore[misc]
    LCLoginRequiredMixin,
    TenantView,
    BaseListView,
):
    filter_form = Select2FilterForm
    fields = ('id', 'text')
    paginate_by = 25

    def get(self, request: LCHttpRequest, *args: Any, **kwargs: Any) -> FastJsonResponse:  # type: ignore[override]
        """
        Return a :class:`..http.FastJsonResponse`.

        Example::

            {
                'results': [
                    {
                        'text': "foo",
                        'id': 123
                    }
                ],
                'more': true
            }

        """
        self.object_list = self.get_queryset()
        context = self.get_context_data()

        return FastJsonResponse({
            'results': list(context['object_list']),
            'pagination': {
                'more': context['page_obj'].has_next(),
            },
        })

    def get_queryset(self) -> QuerySet:
        """
        Return a queryset of {'id': <id>, 'text': <string>} values
        """
        params = {}

        form = self.filter_form(data=self.request.GET)
        if form.is_valid():
            params = form.cleaned_data
        else:
            logger.error('Filter form errors are %s', form.errors)

        return (
            super()
            .get_queryset()
            .annotate(
                text=F('name'),
            )
            .search(
                **params,
            )
            .values(
                *self.fields,
            )
        )


__all__ = (
    'SELECT_OBJECTS_TYPE',
    'LCAutoResponseChoicesView',
    'LCAutoResponseView',
    'LCSelect2ListView',
)
