# flake8: noqa
import pytest
from demoproject.demoapp.models import (
    DemoModel,
    DemoModelCallableDefault,
    DemoModelDefault,
    DemoModelNone,
    Sender1,
    Sender2,
    SenderNotRegistered,
)
from django.forms.models import modelform_factory
from django.urls import reverse
from strategy_field.utils import fqn


def pytest_generate_tests(metafunc):
    func_name = metafunc.function.__name__
    values = ids = []
    if "target" in metafunc.fixturenames:
        if func_name.endswith("_lookup_in"):
            values = [
                lambda o: [fqn(o.sender)],
                lambda o: [o.sender],
                lambda o: [fqn(Sender1), fqn(Sender2)],
                lambda o: [Sender1, Sender2],
            ]
            ids = ["fqn(target.sender)", "target.sender", "fqn(Sender1)", "Sender1"]
        else:
            values = [lambda o: fqn(Sender1), lambda o: Sender1]
            ids = [fqn(Sender1), "Sender1"]
            if "demomodel" in metafunc.fixturenames:
                values.extend([lambda o: fqn(o.sender), lambda o: o.sender])
                ids.extend(["fqn(target.sender)", "target.sender"])

        metafunc.parametrize("target", values, ids=ids)


def test_field():
    d = DemoModel(sender=Sender1)
    assert d.sender == Sender1


@pytest.mark.django_db
def test_model_save(target):
    d = DemoModel(sender=target(None))
    d.save()
    assert d.sender == Sender1


@pytest.mark.django_db
def test_model_save_none():
    d = DemoModelNone(sender=None)
    d.save()
    assert d.sender is None


@pytest.mark.django_db
def test_model_save_default():
    d = DemoModelDefault()
    d.save()
    # registry = d._meta.get_field_by_name('sender')[0].registry
    registry = d._meta.get_field("sender").registry
    assert d.sender == registry[0]


@pytest.mark.django_db
def test_model_save_default_with_callable():
    d = DemoModelCallableDefault()
    d.save()
    # registry = d._meta.get_field_by_name('sender')[0].registry
    registry = d._meta.get_field("sender").registry
    assert d.sender == registry[0]


@pytest.mark.django_db
def test_model_get_or_create(target):
    t = target(None)
    d, __ = DemoModel.objects.get_or_create(sender=t)
    assert d.sender == Sender1


@pytest.mark.django_db
def test_model_load(demomodel):
    d = DemoModel.objects.get(pk=demomodel.pk)
    assert d.sender == Sender1


@pytest.mark.django_db
def test_form(demomodel, registry):
    # demomodel._meta.get_field_by_name('sender')[0].registry = registry
    demomodel._meta.get_field("sender").registry = registry
    form_class = modelform_factory(DemoModel, exclude=[])
    form = form_class(instance=demomodel)
    assert form.fields["sender"].choices[1:] == registry.as_choices()


@pytest.mark.django_db
def test_form_save(demomodel):
    form_class = modelform_factory(DemoModel, exclude=[])
    form = form_class({"sender": fqn(demomodel.sender)}, instance=demomodel)
    assert form.is_valid(), form.errors
    instance = form.save()
    assert instance.sender == demomodel.sender


@pytest.mark.django_db
def test_form_not_valid(demomodel):
    form_class = modelform_factory(DemoModel, exclude=[])
    form = form_class({"sender": fqn(DemoModel)}, instance=demomodel)
    assert not form.is_valid()
    assert form.errors["sender"] == [
        "Select a valid choice. "
        "demoproject.demoapp.models.DemoModel "
        "is not one of the available choices."
    ]


@pytest.mark.django_db
def test_form_default(demomodel):
    form_class = modelform_factory(DemoModel, exclude=[])
    form = form_class(instance=demomodel)
    assert form.fields["sender"].choices == [
        ("", "---------"),
        ("demoproject.demoapp.models.Sender1", "demoproject.demoapp.models.Sender1"),
        ("demoproject.demoapp.models.Sender2", "demoproject.demoapp.models.Sender2"),
    ]

    # assert form.as_table() == u'<tr><th><label for="id_sender">Sender:</label></th>' \
    #                           u'<td><select id="id_sender" name="sender" required>\n' \
    #                           u'<option value="">---------</option>\n' \
    #                           u'<option value="demoproject.demoapp.models.Sender1" selected="selected">demoproject.demoapp.models.Sender1</option>\n' \
    #                           u'<option value="demoproject.demoapp.models.Sender2">demoproject.demoapp.models.Sender2</option>\n</select></td></tr>'


@pytest.mark.django_db
def test_admin_demomodel_add(webapp, admin_user):
    res = webapp.get("/demoapp/demomodel/add/", user=admin_user)
    form = res.forms["demomodel_form"]
    form["sender"] = "demoproject.demoapp.models.Sender1"
    # import pdb; pdb.set_trace()

    form.submit().follow()
    assert (
        DemoModel.objects.filter(sender="demoproject.demoapp.models.Sender1").count()
        == 1
    )


@pytest.mark.django_db
def test_admin_demomodel_edit(webapp, admin_user, demomodel):
    url = reverse("admin:demoapp_demomodel_change", args=[demomodel.pk])
    res = webapp.get(url, user=admin_user)
    form = res.forms["demomodel_form"]
    form["sender"] = "demoproject.demoapp.models.Sender2"
    form.submit().follow()
    assert (
        DemoModel.objects.filter(sender="demoproject.demoapp.models.Sender2").count()
        == 1
    )


@pytest.mark.django_db
def test_admin_demomodel_validate(webapp, admin_user, demomodel):
    url = reverse("admin:demoapp_demomodel_change", args=[demomodel.pk])
    res = webapp.get(url, user=admin_user)
    form = res.forms["demomodel_form"]
    form["sender"].force_value("invalid_strategy_classname")
    res = form.submit()
    assert "Select a valid choice" in res.context["adminform"].form.errors["sender"][0]


@pytest.mark.django_db
def test_demomodel_lookup_equal(demomodel, target):
    assert DemoModel.objects.get(sender=target(demomodel)) == demomodel


@pytest.mark.django_db
def test_demomodel_lookup_contains(demomodel, target):
    assert DemoModel.objects.get(sender__contains=target(demomodel)) == demomodel


@pytest.mark.django_db
def test_demomodel_lookup_in(demomodel, target):
    assert DemoModel.objects.get(sender__in=target(demomodel)) == demomodel


@pytest.mark.django_db
def test_display_attribute(demomodel, registry, monkeypatch):
    monkeypatch.setattr(
        SenderNotRegistered,
        "label",
        classmethod(lambda s: fqn(s).split(".")[-1]),
        raising=False,
    )
    # DemoModel._meta.get_field('sender').display_attribute = 'label'
    DemoModel._meta.get_field("sender").registry = registry
    registry.register(SenderNotRegistered)
    assert registry.as_choices() == [
        ("demoproject.demoapp.models.Sender1", "demoproject.demoapp.models.Sender1"),
        ("demoproject.demoapp.models.Sender2", "demoproject.demoapp.models.Sender2"),
        ("demoproject.demoapp.models.SenderNotRegistered", "SenderNotRegistered"),
    ]

    form_class = modelform_factory(DemoModel, exclude=[])
    form = form_class(instance=demomodel)
    assert form.fields["sender"].choices == [("", "---------")] + registry.as_choices()

    assert form.fields["sender"].choices[3][1] == "SenderNotRegistered"
