from datetime import date

from django.http import Http404
from model_bakery import baker
import pytest

from aircox import filters, models
from aircox.views import mixins
from aircox.test import Interface
from aircox.tests.conftest import req_factory
from .conftest import FakeView


today = date.today()


@pytest.fixture
def redirect_interface():
    iface = Interface.inject(mixins, "redirect", {"__call__": "redirect"})
    yield iface
    iface._irelease()


@pytest.fixture
def date_mixin():
    class Mixin(mixins.GetDateMixin, FakeView):
        pass

    return Mixin()


@pytest.fixture
def parent_mixin():
    class Mixin(mixins.ParentMixin, FakeView):
        parent_model = models.Program

    return Mixin()


@pytest.fixture
def attach_mixin():
    class Mixin(mixins.AttachedToMixin, FakeView):
        attach_to_value = models.StaticPage.ATTACH_TO_HOME

    return Mixin()


@pytest.fixture
def filters_mixin():
    class Mixin(mixins.FiltersMixin, FakeView):
        filterset_class = filters.PageFilters
        queryset = models.Page.objects.all()

    return Mixin()


class TestGetDateMixin:
    req = req_factory.get("/test", {"date": today.strftime("%Y-%m-%d")})

    def test_get_date(self, date_mixin):
        date_mixin.request = self.req
        assert date_mixin.get_date() == today

    def test_get_date_from_kwargs(self, date_mixin):
        date_mixin.request = req_factory.get("/test")
        date_mixin.kwargs = {"date": today}
        assert date_mixin.get_date() == today

    def test_get_date_none_provided(self, date_mixin):
        date_mixin.request = req_factory.get("/test")
        assert date_mixin.get_date() is None

    def test_get_redirect(self, date_mixin, redirect_interface):
        date_mixin.redirect_date_url = "redirect_date_url"
        date_mixin.request = self.req
        assert date_mixin.get() == "redirect"
        assert redirect_interface._trace() == (
            (date_mixin.redirect_date_url,),
            {"date": today.strftime("%Y/%m/%d")},
        )

    def test_get_calls_get_date(self, date_mixin):
        date_mixin.get_date = lambda: today
        date_mixin.get()
        assert date_mixin.date == today


class TestParentMixin:
    req = req_factory.get("/test")

    @pytest.mark.django_db
    def test_get_parent(self, parent_mixin, program):
        parent = parent_mixin.get_parent(self.req, parent_slug=program.slug)
        assert parent.pk == program.pk

    @pytest.mark.django_db
    def test_get_parent_raises_404(self, parent_mixin):
        with pytest.raises(Http404):
            parent_mixin.get_parent(self.req, parent_slug="parent-invalid-slug")

    def test_get_parent_not_parent_model(self, parent_mixin):
        parent_mixin.parent_model = None
        assert parent_mixin.get_parent(self.req) is None

    def test_get_parent_not_parent_url_kwargs(self, parent_mixin):
        assert parent_mixin.get_parent(self.req) is None

    def test_get_calls_parent(self, parent_mixin):
        parent = "parent object"
        parent_mixin.get_parent = lambda *_, **kw: parent
        parent_mixin.get(self.req)
        assert parent_mixin.parent == parent

    @pytest.mark.django_db
    def test_get_queryset_with_parent(self, parent_mixin, program, episodes):
        parent_mixin.queryset = models.Episode.objects.all()
        parent_mixin.parent = program
        episodes_id = {r.id for r in episodes if r.parent_id == program.id}
        query = parent_mixin.get_queryset().values_list("id", flat=True)
        assert set(query) == episodes_id

    def test_get_context_data_with_parent(self, parent_mixin):
        parent_mixin.parent = Interface(cover="parent-cover")
        context = parent_mixin.get_context_data()
        assert context["cover"] == "parent-cover"


class TestAttachedToMixin:
    @pytest.mark.django_db
    def test_get_page_with_attach_to(self, attach_mixin):
        page = baker.make(
            models.StaticPage,
            attach_to=attach_mixin.attach_to_value,
            status=models.StaticPage.STATUS_PUBLISHED,
        )
        assert attach_mixin.get_page() == page


class TestFiltersMixin:
    req = req_factory.get("/test", {"data": True, "page": "page"})

    @pytest.mark.django_db
    def test_get_filterset(self, filters_mixin):
        filterset = filters_mixin.get_filterset({}, models.Page.objects.all())
        assert isinstance(filterset, filters_mixin.filterset_class)

    @pytest.mark.django_db
    def test_get_queryset(self, filters_mixin):
        filterset = Interface(qs="filterset-qs")
        filters_mixin.request = self.req
        filters_mixin.get_filterset = lambda *_, **__: filterset
        assert filters_mixin.get_queryset() == filterset.qs

    def test_get_context_data_valid_filterset(self, filters_mixin):
        filterset = Interface(
            None,
            {"is_valid": True},
            qs="filterset-qs",
            form=Interface(cleaned_data="cleaned_data"),
        )
        filters_mixin.request = self.req
        context = filters_mixin.get_context_data(filterset=filterset)
        assert context["filterset_data"] == "cleaned_data"
        assert dict(context["get_params"]) == {"data": ["True"]}
