"""Test related to Find&elect tab except of searching"""

import pytest
from django.shortcuts import reverse
from django.test import override_settings
from rest_framework.test import APIRequestFactory
from topobank.testing.factories import (SurfaceFactory, TagFactory,
                                        Topography1DFactory, UserFactory)
from topobank.testing.utils import assert_no_form_errors

from ..utils import selected_instances
from ..views import (DEFAULT_SELECT_TAB_STATE, select_surface, select_tag,
                     select_topography, unselect_all, unselect_surface,
                     unselect_tag, unselect_topography)


@pytest.mark.django_db
def test_select_surface():
    user = UserFactory()
    surface1 = SurfaceFactory(creator=user)
    surface2 = SurfaceFactory(creator=user)
    surface3 = SurfaceFactory(creator=user)
    topo3a = Topography1DFactory(surface=surface3)
    Topography1DFactory(surface=surface3)

    factory = APIRequestFactory()
    session = {}

    #
    # First select a single surface
    #
    request = factory.post(reverse("ce_ui:surface-select", kwargs=dict(pk=surface1.pk)))
    request.user = user
    request.session = session

    response = select_surface(request, surface1.pk)

    assert response.status_code == 200

    assert request.session["selection"] == [f"surface-{surface1.pk}"]

    assert selected_instances(request)[1] == [surface1]

    #
    # Then select another
    #
    request = factory.post(reverse("ce_ui:surface-select", kwargs=dict(pk=surface1.pk)))
    request.user = user
    request.session = session

    response = select_surface(request, surface2.pk)

    assert response.status_code == 200

    assert sorted(request.session["selection"]) == [
        f"surface-{surface1.pk}",
        f"surface-{surface2.pk}",
    ]

    assert selected_instances(request)[1] == [surface1, surface2]

    #
    # If a surface is selected after a single topography of this surface
    # was selected, the selection of the single topography should be still there
    #
    request = factory.post(
        reverse("ce_ui:topography-select", kwargs=dict(pk=topo3a.pk))
    )
    request.user = user
    request.session = session

    response = select_topography(request, topo3a.pk)

    assert response.status_code == 200

    assert sorted(request.session["selection"]) == [
        f"surface-{surface1.pk}",
        f"surface-{surface2.pk}",
        f"topography-{topo3a.pk}",
    ]

    assert selected_instances(request)[0] == [topo3a]
    assert selected_instances(request)[1] == [surface1, surface2]

    request = factory.post(reverse("ce_ui:surface-select", kwargs=dict(pk=surface3.pk)))
    request.user = user
    request.session = session

    response = select_surface(request, surface3.pk)

    assert response.status_code == 200

    # the selection for the single topography should still be present
    assert sorted(request.session["selection"]) == [
        f"surface-{surface1.pk}",
        f"surface-{surface2.pk}",
        f"surface-{surface3.pk}",
        f"topography-{topo3a.pk}",
    ]

    assert selected_instances(request)[0] == [topo3a]
    assert selected_instances(request)[1] == [surface1, surface2, surface3]


@pytest.mark.django_db
def test_unselect_surface():
    user = UserFactory()
    surface1 = SurfaceFactory(creator=user)
    surface2 = SurfaceFactory(creator=user)

    factory = APIRequestFactory()
    session = dict(selection=[f"surface-{surface1.pk}", f"surface-{surface2.pk}"])

    #
    # deselect a surface
    #
    request = factory.post(
        reverse("ce_ui:surface-unselect", kwargs=dict(pk=surface1.pk))
    )
    request.user = user
    request.session = session

    response = unselect_surface(request, surface1.pk)

    assert response.status_code == 200

    assert request.session["selection"] == [f"surface-{surface2.pk}"]

    assert selected_instances(request)[1] == [surface2]


@override_settings(DELETE_EXISTING_FILES=True)
@pytest.mark.django_db
def test_try_to_select_surface_but_not_allowed():
    user1 = UserFactory()
    user2 = UserFactory()
    surface1 = SurfaceFactory(creator=user1)

    factory = APIRequestFactory()
    session = {}

    request = factory.post(reverse("ce_ui:surface-select", kwargs=dict(pk=surface1.pk)))
    request.user = user2
    request.session = session

    response = select_surface(request, surface1.pk)

    assert response.status_code == 403


@pytest.mark.django_db(transaction=True)
def test_try_to_select_topography_but_not_allowed():
    user1 = UserFactory()
    user2 = UserFactory()
    surface1 = SurfaceFactory(creator=user1)
    topo1 = Topography1DFactory(surface=surface1)

    factory = APIRequestFactory()
    session = {}

    request = factory.post(reverse("ce_ui:topography-select", kwargs=dict(pk=topo1.pk)))
    request.user = user2
    request.session = session

    response = select_topography(request, topo1.pk)

    assert response.status_code == 403

    # if user 1 shares the surface with user 2, it is allowed
    surface1.grant_permission(user2)
    response = select_topography(request, topo1.pk)
    assert response.status_code == 200


@pytest.mark.django_db(transaction=True)
def test_try_to_select_tag_but_not_allowed():
    user1 = UserFactory()
    user2 = UserFactory()

    tag1 = TagFactory()
    SurfaceFactory(creator=user1, tags=[tag1])

    factory = APIRequestFactory()
    session = {}

    request = factory.post(reverse("ce_ui:tag-select", kwargs=dict(pk=tag1.pk)))
    request.user = user2
    request.session = session

    response = select_tag(request, tag1.pk)

    # not allowed, because tag is not used by user 2
    assert response.status_code == 403

    # If user 2 also uses this tag, it can be selected
    SurfaceFactory(creator=user2, tags=[tag1])

    response = select_tag(request, tag1.pk)
    assert response.status_code == 200


@pytest.mark.django_db
def test_select_topography():
    user = UserFactory()
    surface1 = SurfaceFactory(creator=user)
    topo1a = Topography1DFactory(surface=surface1)
    topo1b = Topography1DFactory(surface=surface1)
    topo1c = Topography1DFactory(surface=surface1)
    SurfaceFactory(creator=user)

    factory = APIRequestFactory()
    session = {}

    #
    # First select a single surface
    #
    request = factory.post(
        reverse("ce_ui:topography-select", kwargs=dict(pk=topo1a.pk))
    )
    request.user = user
    request.session = session

    response = select_topography(request, topo1a.pk)

    assert response.status_code == 200

    assert request.session["selection"] == [f"topography-{topo1a.pk}"]

    assert selected_instances(request)[0] == [topo1a]

    #
    # Then select a second one
    #
    request = factory.post(
        reverse("ce_ui:topography-select", kwargs=dict(pk=topo1b.pk))
    )
    request.user = user
    request.session = session

    response = select_topography(request, topo1b.pk)

    assert response.status_code == 200

    assert sorted(request.session["selection"]) == [
        f"topography-{topo1a.pk}",
        f"topography-{topo1b.pk}",
    ]

    assert selected_instances(request)[0] == [topo1a, topo1b]

    #
    # When selecting all topographies of a surface, the surface should not be selected
    #
    request = factory.post(
        reverse("ce_ui:topography-select", kwargs=dict(pk=topo1c.pk))
    )
    request.user = user
    request.session = session

    response = select_topography(request, topo1c.pk)

    assert response.status_code == 200

    assert sorted(request.session["selection"]) == [
        f"topography-{topo1a.pk}",
        f"topography-{topo1b.pk}",
        f"topography-{topo1c.pk}",
    ]

    assert selected_instances(request)[0] == [topo1a, topo1b, topo1c]

    assert (
        selected_instances(request)[1] == []
    )  # we only want explicitly selected objects now

    #
    # When selecting some arbitrary topography, a permission denied should show up
    #
    invalid_pk = 99999999999
    request = factory.post(
        reverse("ce_ui:topography-select", kwargs=dict(pk=invalid_pk))
    )
    request.user = user
    request.session = session

    response = select_topography(request, invalid_pk)

    assert response.status_code == 403


@pytest.mark.django_db
def test_unselect_topography():
    user = UserFactory()
    surface1 = SurfaceFactory(creator=user)
    topo1a = Topography1DFactory(surface=surface1)
    topo1b = Topography1DFactory(surface=surface1)
    surface2 = SurfaceFactory(creator=user)

    factory = APIRequestFactory()
    session = dict(
        selection=[
            f"surface-{surface1.pk}",
            f"surface-{surface2.pk}",
            f"topography-{topo1b.pk}",
        ]
    )

    #
    # deselect a topography
    #
    request = factory.post(
        reverse("ce_ui:topography-unselect", kwargs=dict(pk=topo1a.pk))
    )
    request.user = user
    request.session = session

    response = unselect_topography(request, topo1a.pk)

    assert response.status_code == 200

    # This has no effect, since the topography was not explicitly selected
    assert sorted(request.session["selection"]) == sorted(
        [f"surface-{surface1.pk}", f"surface-{surface2.pk}", f"topography-{topo1b.pk}"]
    )

    assert selected_instances(request)[0] == [topo1b]
    assert selected_instances(request)[1] == [surface1, surface2]

    #
    # Now remove topo1b
    #
    request = factory.post(
        reverse("ce_ui:topography-unselect", kwargs=dict(pk=topo1b.pk))
    )
    request.user = user
    request.session = session

    response = unselect_topography(request, topo1b.pk)

    assert response.status_code == 200
    assert sorted(request.session["selection"]) == [
        f"surface-{surface1.pk}",
        f"surface-{surface2.pk}",
    ]
    assert selected_instances(request)[0] == []
    assert selected_instances(request)[1] == [surface1, surface2]


#
# Tests for selection of tags
#
@pytest.mark.django_db
def test_select_tag():
    user = UserFactory()

    tag1 = TagFactory()
    tag2 = TagFactory()

    # we use the tags, so the user is allowed to select it
    SurfaceFactory(creator=user, tags=[tag1, tag2])

    factory = APIRequestFactory()
    session = {}

    #
    # First select a single tag
    #
    request = factory.post(reverse("ce_ui:tag-select", kwargs=dict(pk=tag1.pk)))
    request.user = user
    request.session = session

    response = select_tag(request, tag1.pk)

    assert response.status_code == 200

    assert request.session["selection"] == [f"tag-{tag1.pk}"]

    assert selected_instances(request)[2] == [tag1]

    #
    # Then select another
    #
    request = factory.post(reverse("ce_ui:tag-select", kwargs=dict(pk=tag2.pk)))
    request.user = user
    request.session = session

    response = select_tag(request, tag2.pk)

    assert response.status_code == 200

    assert sorted(request.session["selection"]) == [f"tag-{tag1.pk}", f"tag-{tag2.pk}"]

    assert selected_instances(request)[2] == [tag1, tag2]


@pytest.mark.django_db
def test_unselect_tag():
    user = UserFactory()

    tag1 = TagFactory()
    tag2 = TagFactory()

    # we use the tags, so the user is allowed to select it
    SurfaceFactory(creator=user, tags=[tag1, tag2])

    factory = APIRequestFactory()
    session = dict(selection=[f"tag-{tag1.pk}", f"tag-{tag2.pk}"])

    #
    # deselect a tag
    #
    request = factory.post(reverse("ce_ui:tag-unselect", kwargs=dict(pk=tag1.pk)))
    request.user = user
    request.session = session

    response = unselect_tag(request, tag1.pk)

    assert response.status_code == 200

    assert request.session["selection"] == [f"tag-{tag2.pk}"]

    assert selected_instances(request)[2] == [tag2]


@pytest.mark.django_db
def test_unselect_all():
    user = UserFactory()

    tag1 = TagFactory()

    # we use the tags, so the user is allowed to select it
    surface1 = SurfaceFactory(creator=user, tags=[tag1])
    topo1 = Topography1DFactory(surface=surface1)

    factory = APIRequestFactory()
    session = dict(
        selection=[f"tag-{tag1.pk}", f"surface-{surface1.pk}", f"topography-{topo1.pk}"]
    )

    #
    # deselect all
    #
    request = factory.post(reverse("ce_ui:unselect-all"))
    request.user = user
    request.session = session

    response = unselect_all(request)

    assert response.status_code == 200

    assert request.session["selection"] == []


@pytest.mark.django_db
def test_select_tab_state_should_be_default_after_login(client, orcid_socialapp):
    # first request the site anonymously .. select tab state is set to that of
    # an anonymous user
    response = client.get(reverse("ce_ui:select"))
    assert response.context["select_tab_state"]["sharing_status"] == "published"

    # Then login as authenticated user
    password = "abcd"
    user = UserFactory(password=password)

    # we use a real request in order to trigger the signal
    response = client.post(
        reverse("account_login"),
        {
            "password": password,
            "login": user.username,
        },
    )

    assert response.status_code == 302
    assert_no_form_errors(response)

    response = client.get(reverse("ce_ui:select"))

    assert response.context["select_tab_state"] == DEFAULT_SELECT_TAB_STATE


@pytest.mark.django_db
def test_select_tab_state_should_be_default_after_search(
    client, handle_usage_statistics
):
    state_before_search = DEFAULT_SELECT_TAB_STATE.copy()
    state_before_search["current_page"] = 2

    user = UserFactory()
    client.force_login(user)
    client.session["select_tab_state"] = state_before_search

    response = client.get(
        reverse("ce_ui:select"), data={"search": "what I want to find"}
    )

    exp_state_after_search = DEFAULT_SELECT_TAB_STATE.copy()
    exp_state_after_search["search_term"] = "what I want to find"

    assert exp_state_after_search["current_page"] == 1

    assert response.context["select_tab_state"] == exp_state_after_search
