import django_filters

from .models import (
    Case,
    CaseCollection,
    Condition,
    Finding,
    PathologicalProcess,
    Presentation,
    QuestionSchema,
    Series,
    Structure,
    Subspecialty,
    NormalCase,
    Resource,
    Site,
)
from django.contrib.auth.models import User
from django.db.models import Q


def get_authors(request):
    if request is None:
        return User.objects.none()

    if not request.user.groups.filter(name="atlas_editor").exists():
        queryset = User.objects.filter(id=request.user.id)
    else:
        queryset = User.objects.all()

    return queryset


def get_cases(request):
    if request is None:
        return Case.objects.none()

    if not request.user.groups.filter(name="atlas_editor").exists():
        queryset = Case.objects.filter(author__id=request.user.id)
    else:
        queryset = Case.objects.all()

    return queryset


def get_series(request):
    if request is None:
        return Series.objects.none()

    if not request.user.groups.filter(name="atlas_editor").exists():
        queryset = Series.objects.filter(author__id=request.user.id)
    else:
        queryset = Series.objects.all()

    queryset = queryset.prefetch_related("examination", "plane")

    return queryset


class CaseCollectionFilter(django_filters.FilterSet):
    author = django_filters.ModelMultipleChoiceFilter(
        queryset=get_authors, null_label="No author"
    )
    cases = django_filters.ModelMultipleChoiceFilter(
        queryset=get_cases, null_label="No cases"
    )

    class Meta:
        model = CaseCollection
        fields = (
            "archive",
            "author",
        )  # "site",

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        if not request.user.groups.filter(name="atlas_editor").exists():
            queryset = queryset.prefetch_related("author").filter(
                author__id=request.user.id
            )
        super(CaseCollectionFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass


class CaseFilter(django_filters.FilterSet):
    author = django_filters.ModelMultipleChoiceFilter(
        queryset=get_authors, null_label="No author"
    )
    series = django_filters.ModelMultipleChoiceFilter(
        queryset=get_series, null_label="No series"
    )
    differential_case = django_filters.ModelMultipleChoiceFilter(
        label="Differential",
        field_name="differentialcase__condition",
        # to_field_name="differential",
        queryset=Condition.objects.all(),
    )

    class Meta:
        model = Case
        fields = (
            "created_date",
            "open_access",
            "author",
            "differential_case",
        )  # "site",

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        if not request.user.groups.filter(name="atlas_editor").exists():
            queryset = queryset.prefetch_related("author").filter(
                (Q(author__id=request.user.id) | Q(open_access=True))
            )
        super(CaseFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass


class SeriesFilter(django_filters.FilterSet):
    case = django_filters.ModelMultipleChoiceFilter(
        queryset=get_cases, null_label="No case"
    )
    author = django_filters.ModelMultipleChoiceFilter(
        queryset=get_authors, null_label="No author"
    )

    class Meta:
        model = Series
        fields = ("modality", "case", "examination", "author")

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        if not request.user.groups.filter(name="atlas_editor").exists():
            # queryset = queryset.filter(open_access=True) | queryset.filter(author__id=request.user.id)
            queryset = queryset.filter(author__id=request.user.id)
        super(SeriesFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass


class ConditionFilter(django_filters.FilterSet):
    # Replace legacy `primary` filter with `is_canonical` and provide a
    # `synonym` filter which will filter by the canonical group of the
    # selected condition.
    is_canonical = django_filters.BooleanFilter(method="filter_is_canonical", label="Canonical")
    synonym = django_filters.ModelChoiceFilter(queryset=Condition.objects.all(), method="filter_synonym", label="Synonym group")

    class Meta:
        model = Condition
        fields = {
            "name": ["icontains"],
            "subspecialty": ["exact"],
            "rcr_curriculum": ["exact"],
        }

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        super(ConditionFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass

    def filter_is_canonical(self, queryset, name, value):
        if value in (True, "True", "true", 1, "1"):
            return queryset.filter(canonical__isnull=True)
        if value in (False, "False", "false", 0, "0"):
            return queryset.filter(canonical__isnull=False)
        return queryset

    def filter_synonym(self, queryset, name, value):
        # value is a Condition instance selected in the filter. Return all
        # Conditions that belong to the same canonical group as `value`.
        if not value:
            return queryset
        master = value.canonical if value.canonical else value
        return queryset.filter(Q(canonical=master) | Q(pk=master.pk))


class FindingFilter(django_filters.FilterSet):
    # Replace legacy `primary`/`synonym` with canonical-based filters
    is_canonical = django_filters.BooleanFilter(method="filter_is_canonical", label="Canonical")
    synonym = django_filters.ModelChoiceFilter(queryset=Finding.objects.all(), method="filter_synonym", label="Synonym group")

    class Meta:
        model = Finding
        fields = {
            "name": ["icontains"],
        }

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        super(FindingFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass

    def filter_is_canonical(self, queryset, name, value):
        if value in (True, "True", "true", 1, "1"):
            return queryset.filter(canonical__isnull=True)
        if value in (False, "False", "false", 0, "0"):
            return queryset.filter(canonical__isnull=False)
        return queryset

    def filter_synonym(self, queryset, name, value):
        # value is a Finding instance selected in the filter. Return all
        # Findings that belong to the same canonical group as `value`.
        if not value:
            return queryset
        master = value.canonical if value.canonical else value
        return queryset.filter(Q(canonical=master) | Q(pk=master.pk))


class StructureFilter(django_filters.FilterSet):
    is_canonical = django_filters.BooleanFilter(method="filter_is_canonical", label="Canonical")
    synonym = django_filters.ModelChoiceFilter(queryset=Structure.objects.all(), method="filter_synonym", label="Synonym group")

    class Meta:
        model = Structure
        fields = {"name": ["icontains"]}

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        super(StructureFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass

    def filter_is_canonical(self, queryset, name, value):
        if value in (True, "True", "true", 1, "1"):
            return queryset.filter(canonical__isnull=True)
        if value in (False, "False", "false", 0, "0"):
            return queryset.filter(canonical__isnull=False)
        return queryset

    def filter_synonym(self, queryset, name, value):
        if not value:
            return queryset
        master = value.canonical if value.canonical else value
        return queryset.filter(Q(canonical=master) | Q(pk=master.pk))


class PresentationFilter(django_filters.FilterSet):
    class Meta:
        model = Presentation
        fields = ("name", "subspecialty")

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        super(PresentationFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass


class PathologicalProcessFilter(django_filters.FilterSet):
    class Meta:
        model = PathologicalProcess
        fields = ("name",)

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        super(PathologicalProcessFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass


class SubspecialtyFilter(django_filters.FilterSet):
    class Meta:
        model = Subspecialty
        fields = ("name",)

    def __init__(
        self,
        data=None,
        queryset=None,
        prefix=None,
        strict=None,
        user=None,
        request=None,
    ):
        super(SubspecialtyFilter, self).__init__(
            data=data, queryset=queryset, prefix=prefix, request=request
        )
        pass


class NormalCaseFilter(django_filters.FilterSet):
    """FilterSet for the NormalCase model allowing filtering by
    examination, modality and nearest-year age ranges.
    """
    examination = django_filters.ModelChoiceFilter(queryset=Series.objects.none())
    modality = django_filters.ModelChoiceFilter(queryset=Series.objects.none())
    # Accept min/max as years in the UI but filter against the canonical
    # `age_days` field by converting years -> days.
    min_age = django_filters.NumberFilter(label="Min age", method="filter_min_age")
    max_age = django_filters.NumberFilter(label="Max age", method="filter_max_age")

    class Meta:
        model = NormalCase
        fields = ("examination", "modality", "age_days")

    def __init__(self, data=None, queryset=None, prefix=None, strict=None, user=None, request=None):
        # Use the global Examination and Modality querysets from the Series model
        from generic.models import Examination, Modality

        # Set up the actual querysets for the filters
        self.base_filters.get("examination").queryset = Examination.objects.all()
        self.base_filters.get("modality").queryset = Modality.objects.all()

        # If not an atlas editor restrict to cases authored by the user or open access cases
        if request is not None and not request.user.groups.filter(name="atlas_editor").exists():
            from django.db.models import Q

            if queryset is None:
                queryset = NormalCase.objects.select_related("case")
            queryset = queryset.filter((Q(case__author__id=request.user.id) | Q(case__open_access=True)))

        super(NormalCaseFilter, self).__init__(data=data, queryset=queryset, prefix=prefix, request=request)

    def filter_min_age(self, queryset, name, value):
        try:
            days = int(float(value) * 365.25)
        except Exception:
            return queryset
        return queryset.filter(age_days__gte=days)

    def filter_max_age(self, queryset, name, value):
        try:
            days = int(float(value) * 365.25)
        except Exception:
            return queryset
        return queryset.filter(age_days__lte=days)


class ResourceFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
    site = django_filters.ModelMultipleChoiceFilter(field_name='sites', queryset=lambda request: Site.objects.all() if True else Site.objects.none())
    subspecialty = django_filters.ModelMultipleChoiceFilter(field_name='subspecialty', queryset=Subspecialty.objects.all())
    author = django_filters.ModelMultipleChoiceFilter(queryset=get_authors, null_label='No author')
    has_file = django_filters.BooleanFilter(method='filter_has_file', label='Has file')

    class Meta:
        model = Resource
        fields = ('name', 'site', 'subspecialty', 'author')

    def filter_has_file(self, queryset, name, value):
        if value is True:
            return queryset.exclude(file__isnull=True).exclude(file__exact='')
        elif value is False:
            return queryset.filter(file__isnull=True) | queryset.filter(file__exact='')
        return queryset

class QuestionSchemaFilter(django_filters.FilterSet):
    class Meta:
        model = QuestionSchema
        fields = ("name", "description")