import json
import pytest
from django.urls import reverse

from rich.pretty import pprint

from bs4 import BeautifulSoup

from django.contrib.auth.models import Group

from rapids.views import GenericExamViews as RapidsExamViews
from rapids.views import GenericExamViews as LongsExamViews
from sbas.views import GenericExamViews as SbaExamViews
from longs.views import GenericExamViews as LongsExamViews
from physics.views import GenericExamViews as PhysicExamViews
from anatomy.views import GenericExamViews as AnatomyExamViews

from generic.models import Supervisor
from loguru import logger

APP_NAMES = ("rapids", "anatomy", "longs", "physics", "sbas")

JSON_APPS = ("rapids", "anatomy", "longs")

EXAM_VIEWS = (
    RapidsExamViews,
    LongsExamViews,
    SbaExamViews,
    PhysicExamViews,
    AnatomyExamViews,
)

JSON_VIEWS = (
    RapidsExamViews,
    LongsExamViews,
    AnatomyExamViews,
)


@pytest.fixture
def create_superuser(db, django_user_model):
    return django_user_model.objects.create_superuser(
        "admin", "ross@xkjq.uk", "adminpassword"
    )

@pytest.fixture
def create_cid_manager(db, django_user_model):
    user = django_user_model.objects.create_user(
        "cid_user", "cid@user.com", "password1234"
    )
    g = Group.objects.create(name="cid_user_manager")
    g.save()
    user.groups.add(g)
    return user

@pytest.fixture
def create_basic_user(db, django_user_model):
    user = django_user_model.objects.create_user(
        "basicuser", "basic@user.com", "password1234"
    )
    return user

@pytest.fixture(params=EXAM_VIEWS)
def set_up_exams(request, django_user_model, create_superuser):
    basic_user = django_user_model.objects.create_user(
        "basicuser", "user@user.net", "pass2"
    )

    e1 = request.param.Exam.objects.create(name="test exam", exam_mode=True)
    e = request.param.Exam.objects.create(name="test exam basic user", exam_mode=True)
    e.author.add(basic_user)
    return request.param


# We only test JSON functionality with this so limit the views
@pytest.fixture(params=JSON_VIEWS)
def set_up_packets(request):
    e = request.param.Exam.objects.create(name="test packet", exam_mode=False, active=True)
    return request.param


# @pytest.fixture
# def create_exam(db):
#    return Exam.objects.create(name="test exam", exam_mode=True)

# Very basic tests, make sure we can hit key urls without error

def test_index2(client, set_up_exams):
    client.login(username="admin", password="adminpassword")
    response = client.get(reverse(f"{set_up_exams.app_name}:index"))
    assert response.status_code == 200

    assert (
        BeautifulSoup(response.content, "html.parser")
        .find("h4", {"class": "exam-number-title"})
        .getText()
        == "2 exams found."
    )


def test_packet_json_access(db, client, set_up_packets):
    """
    Simple test to check that json is retrievable from non exam mode packets
    """
    if set_up_packets.app_name in JSON_APPS:
        exam_id = set_up_packets.Exam.objects.get(name="test packet").pk

        response = client.get(reverse(f"{set_up_packets.app_name}:exam_json", args=[exam_id]))
        assert response.status_code == 200

        exam_json = json.loads(response.content)

        # Remove the s at the end of the app name
        # TODO: unify this....
        #assert exam_json["eid"] == f"{set_up_packets.app_name/{exam_id}"
        assert exam_json["eid"].split("/")[-1] == f"{exam_id}"
        print(exam_json)
        assert exam_json["exam_mode"] == False
        assert exam_json["exam_name"] == "test packet"
        assert len(exam_json["questions"]) == 0

def test_overview(client, set_up_exams):
    client.login(username="admin", password="adminpassword")
    
    e1 = set_up_exams.Exam.objects.get(name="test exam").pk
    e2 = set_up_exams.Exam.objects.get(name="test exam basic user").pk

    response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e1]))
    assert response.status_code == 200

    response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e2]))
    assert response.status_code == 200

    client.login(username="basicuser", password="pass2")
    response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e1]))
    assert response.status_code == 403

    response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e2]))
    assert response.status_code == 200


@pytest.mark.parametrize("exam_views", EXAM_VIEWS)
def test_index(client, create_superuser, exam_views, django_user_model):

    basic_user = django_user_model.objects.create_user(
        "basicuser", "user@user.net", "pass2"
    )

    e1 = exam_views.Exam.objects.create(name="test exam", exam_mode=True)
    e = exam_views.Exam.objects.create(name="test exam 2", exam_mode=True)
    e.author.add(basic_user)

    client.login(username="admin", password="adminpassword")
    response = client.get(reverse(f"{exam_views.app_name}:index"))
    assert response.status_code == 200

    assert (
        BeautifulSoup(response.content, "html.parser")
        .find("h4", {"class": "exam-number-title"})
        .getText()
        == "2 exams found."
    )

    
    response = client.get(reverse(f"{exam_views.app_name}:exam_list"))
    assert response.status_code == 200
    assert (
        len(BeautifulSoup(response.content, "html.parser")
        .select("a.exam-name"))
        == 2
    )

    if exam_views.app_name in JSON_APPS:
        response = client.get(reverse(f"{exam_views.app_name}:active_exams"))

        json_response = json.loads(response.content)["exams"]

        assert len(json_response) == 2

        for json_exam in json_response:
            eid_app_type, eid = json_exam["eid"].split("/")
            # hack for long / longs mismathc
            assert (
                eid_app_type == exam_views.app_name
                or eid_app_type == exam_views.app_name[:-1]
            )

            assert exam_views.Exam.objects.get(pk=int(eid)).name == json_exam["name"]

        add_user_response = client.post(
            reverse(f"{exam_views.app_name}:exam_json_edit", args=(e1.pk,)),
            {"edit_user_user": basic_user.pk, "add": "true"},
            HTTP_X_REQUESTED_WITH="XMLHttpRequest",
        )


        assert add_user_response.status_code == 200
        assert add_user_response.headers["Content-Type"].startswith("text/html")
        assert add_user_response.headers["HX-Trigger"] == json.dumps(
            {"user_toggled": {"pk": basic_user.pk, "added": True}}
        )
        assert e1.valid_user_users.filter(pk=basic_user.pk).exists()
        assert f'data-pk="{basic_user.pk}"' in add_user_response.content.decode()

    client.logout()

    client.force_login(basic_user)

    response = client.get(reverse(f"{exam_views.app_name}:index"))
    assert response.status_code == 200
    assert (
        BeautifulSoup(response.content, "html.parser")
        .find("h4", {"class": "exam-number-title"})
        .getText()
        == "1 exams found."
    )

    response = client.get(reverse(f"{exam_views.app_name}:exam_list"))

    assert (
        len(BeautifulSoup(response.content, "html.parser")
        .select("a.exam-name"))
        == 1
    )


    if exam_views.app_name in JSON_APPS:
        response = client.get(reverse(f"{exam_views.app_name}:active_exams"))

        json_response = json.loads(response.content)["exams"]

        assert len(json_response) == 1

        e1.active = True
        e1.save()

        response = client.get(reverse(f"{exam_views.app_name}:active_exams"))

        json_response = json.loads(response.content)["exams"]

        assert len(json_response) == 2

def test_cid_management(client, create_cid_manager, django_user_model, create_basic_user):
    urls = [
        reverse(f"people"),
        #reverse(f"generic:update_cid"),
        #reverse(f"generic:update_cid_exams"),
        reverse(f"generic:manage_cids"),
        reverse(f"generic:manage_cid_exams"),
        reverse(f"generic:cid_group_view"),
        reverse(f"generic:cid_group_view_all"),
        reverse(f"generic:cid_group_create"),
        reverse(f"generic:supervisor"),
    ]

    for url in urls:
        response = client.get(url)
        # Some urls will try to redirect to a login page
        if response.status_code == 302:
            assert "accounts/login/" in response.url
        else:
            # Others just forbid
            assert response.status_code == 403

    pass

    basic_user = create_basic_user
    client.force_login(basic_user)
    for url in urls:
        # When logged in there should be no redirection (if access denied)
        response = client.get(url)

        assert response.status_code == 403

    cid_manager = create_cid_manager

    client.force_login(cid_manager)

    for url in urls:
        response = client.get(url)
        assert response.status_code == 200

    pass


def create_supervisor(db, faker):
    return Supervisor.objects.create(email=faker.email(), name=faker.name(), active=True)

def test_supervisor_management(db, faker, client, create_cid_manager, django_user_model, create_basic_user ):
    s1 = create_supervisor(db, faker)
    s2 = create_supervisor(db, faker)
    print(s1)
    print(s2)


    basic_user = create_basic_user
    client.force_login(basic_user)

    response = client.get(reverse(f"generic:supervisor_detail", kwargs={"pk": s1.pk}))

    assert response.status_code == 403
    response = client.get(reverse(f"generic:supervisor"))
    assert response.status_code == 403

    # Check we can not delete
    response = client.post(reverse(f"generic:supervisor_delete", kwargs={"pk": s2.pk}))
    assert response.status_code == 403

    cid_manager = create_cid_manager
    client.force_login(cid_manager)

    response = client.get(reverse(f"generic:supervisor"))
    assert response.status_code == 200

    soup = BeautifulSoup(response.content, "html.parser")
    assert Supervisor.objects.count() == 2

    response = client.get(reverse(f"generic:supervisor_detail", kwargs={"pk": s1.pk}))
    soup = BeautifulSoup(response.content, "html.parser")
    assert soup.find(id="name").text == s1.name
    assert soup.find(id="email").text == s1.email

    new_email = "new@email.com"
    response = client.post(reverse(f"generic:supervisor_edit", kwargs={"pk": s1.pk}), {"email":new_email, "name": s1.name, "active": s1.active})

    soup = BeautifulSoup(response.content, "html.parser")
    assert response.status_code == 302

    response = client.get(reverse(f"generic:supervisor_detail", kwargs={"pk": s1.pk}))
    soup = BeautifulSoup(response.content, "html.parser")
    assert soup.find(id="name").text == s1.name
    assert soup.find(id="email").text == new_email

    # Check we can delete
    response = client.post(reverse(f"generic:supervisor_delete", kwargs={"pk": s2.pk}))
    assert response.status_code == 302
    response = client.get(reverse(f"generic:supervisor"))
    assert response.status_code == 200

    soup = BeautifulSoup(response.content, "html.parser")
    assert Supervisor.objects.filter(pk=s2.pk).exists() is False
    assert Supervisor.objects.filter(pk=s1.pk).exists() is True
    #assert len(soup.find(id="supervisor-list").find_all("li")) == 1

    response = client.post(reverse(f"generic:supervisor_create"), {"email":faker.email(), "name": faker.name(), "active": True})
    assert response.status_code == 302

    response = client.get(reverse(f"generic:supervisor"))
    assert response.status_code == 200

    soup = BeautifulSoup(response.content, "html.parser")
    rows = soup.select('#supervisor-table > tbody > tr')
    assert len(rows) == 2
    assert Supervisor.objects.count() == 2


def test_privacy(db, client):
    response = client.get(reverse(f"privacy"))
    assert response.status_code == 200

    soup = BeautifulSoup(response.content, "html.parser")
    assert "Privacy Policy" in soup.find("h1").text

def test_cookies(db, client):
    response = client.get(reverse(f"cookie_consent_cookie_group_list"))
    assert response.status_code == 200

    soup = BeautifulSoup(response.content, "html.parser")
    assert "Cookies" in soup.find("h1").text

    # Check our 2 cookies are displayed (we would have to add them for this to work)
    #assert "csrftoken" in [i.text for i in soup.find_all("th")]
    #assert "sessionid" in [i.text for i in soup.find_all("th")]


def test_rapids_mark2_views(db, client, create_superuser):
    client.force_login(create_superuser)
    from rapids.models import Exam, Rapid
    exam = Exam.objects.create(name="Test Exam", exam_mode=True)
    rapid = Rapid.objects.create(laterality=Rapid.NONE, normal=True)
    exam.exam_questions.add(rapid)

    # 1. Test mark2_overview
    url_overview = reverse("rapids:mark2_overview", kwargs={"pk": exam.pk})
    resp = client.get(url_overview)
    assert resp.status_code == 200

    # 2. Test mark2 question view
    url_mark2 = reverse("rapids:mark2", kwargs={"exam_pk": exam.pk, "sk": 0})
    resp = client.get(url_mark2)
    assert resp.status_code == 200

    # 3. Test mark2_exam_marked partial
    url_exam_marked = reverse("rapids:mark2_exam_marked", kwargs={"exam_pk": exam.pk, "sk": 0})
    resp = client.get(url_exam_marked)
    assert resp.status_code == 200

    # 4. Test mark2_question_marked partial
    url_question_marked = reverse("rapids:mark2_question_marked", kwargs={"pk": rapid.pk})
    resp = client.get(url_question_marked)
    assert resp.status_code == 200

