
from django.urls import reverse
from django.core import mail

from rich.pretty import pprint

from bs4 import BeautifulSoup

from django.contrib.auth.models import Group
import pytest


@pytest.fixture
def create_users(db, django_user_model):
    user1 = django_user_model.objects.create_user(
        "user1", "user1@user.com", "password"
    )
    user2 = django_user_model.objects.create_user(
        "user2", "user2@user.com", "password"
    )
    #g = Group.objects.create(name="cid_user_manager")
    #g.save()
    #user.groups.add(g)
    return user1, user2

@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

def test_profile_access_and_management(db, client, create_users, create_cid_manager):

    response = client.get(reverse(f"profile"))

    # should redirect to login page
    assert response.status_code == 302
    assert "accounts/login/" in response.url
    # Test login

    client.login(username="user1", password="password")

    response = client.get(reverse(f"profile"))

    assert response.status_code == 200

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

    assert soup.find(id="username").text == "user1"
    assert soup.find(id="email").text == "user1@user.com"

    # Check the links to change password and update profile
    password_url = soup.find(id="password-change")["href"]
    print(password_url)
    password_change_response = client.get(password_url)
    assert password_change_response.status_code == 200

    password_soup = BeautifulSoup(password_change_response.content, "html.parser")
    assert "Old password" in password_soup.text
    assert "New password" in password_soup.text

    update_profile = soup.find(id="update-profile-link")["href"]

    assert update_profile == reverse("account_profile_update", kwargs={"slug": "user1"})

    # check we can't access another users profile 
    response_fail = client.get(reverse("account_profile_update", kwargs={"slug": "user2"}))
    assert response_fail.status_code == 404
    print(response_fail.content)

    # And that we can access our own
    update_profile_response = client.get(reverse("account_profile_update", kwargs={"slug": "user1"}))
    assert update_profile_response.status_code == 200

    assert "peninsula_trainee" not in BeautifulSoup(update_profile_response.content, "html.parser").text

    update_response = client.post(reverse("account_profile_update", kwargs={"slug": "user1"}), {
        "registration_number": 1234567
    })
    assert update_response.status_code == 302
    # we reirect back to profile page on form submission
    assert update_response.url == reverse("profile")

    # check that the form has updated
    update_profile_response = client.get(reverse("account_profile_update", kwargs={"slug": "user1"}))
    assert BeautifulSoup(update_profile_response.content, "html.parser").find(id="id_registration_number")["value"]


@pytest.mark.django_db
def test_create_user_reuses_existing_account(client, create_cid_manager, django_user_model):
    existing_user = django_user_model.objects.create_user(
        username="existing@example.com",
        email="existing@example.com",
        password="password123",
        first_name="Old",
        last_name="Name",
    )

    client.force_login(create_cid_manager)

    response = client.post(
        reverse("create_user"),
        {
            "username": "existing@example.com",
            "first_name": "Updated",
            "last_name": "User",
            "grade": "",
        },
    )

    assert response.status_code == 302
    assert response.url == reverse("accounts_list")

    existing_user.refresh_from_db()
    assert existing_user.first_name == "Updated"
    assert existing_user.last_name == "User"


@pytest.mark.django_db
def test_cid_manager_can_send_password_reset_from_account_profile(client, create_cid_manager, django_user_model):
    managed_user = django_user_model.objects.create_user(
        username="managed-user",
        email="managed@example.com",
        password="password123",
    )

    client.force_login(create_cid_manager)
    response = client.post(
        reverse("account_send_password_reset", kwargs={"slug": managed_user.username}),
        {"next": reverse("account_profile", kwargs={"slug": managed_user.username})},
    )

    assert response.status_code == 302
    assert "password_reset_action=sent" in response.url
    assert len(mail.outbox) == 1
    assert mail.outbox[0].to == ["managed@example.com"]


@pytest.mark.django_db
def test_people_page_and_user_search(client, create_users, create_cid_manager):
    # Log in as cid_user_manager
    client.force_login(create_cid_manager)

    # 1. Test standard GET request to /people page
    response = client.get(reverse("people"))
    assert response.status_code == 200
    assert b"Quick User Search" in response.content
    assert b"hx-indicator=\"#search-indicator\"" in response.content

    # 2. Test HTMX GET request with empty query
    response_empty = client.get(
        reverse("people"),
        {"q": ""},
        HTTP_HX_REQUEST="true",
    )
    assert response_empty.status_code == 200
    assert response_empty.content == b""

    # 3. Test HTMX GET request with matching query
    user1, user2 = create_users
    response_search = client.get(
        reverse("people"),
        {"q": "user1"},
        HTTP_HX_REQUEST="true",
    )
    assert response_search.status_code == 200
    assert b"user1" in response_search.content
    assert b"user1@user.com" in response_search.content
    assert b"Actions" in response_search.content

    # 4. Test HTMX GET request with no matching users
    response_no_match = client.get(
        reverse("people"),
        {"q": "nonexistent_username_xyz"},
        HTTP_HX_REQUEST="true",
    )
    assert response_no_match.status_code == 200
    assert b"No users found matching" in response_no_match.content


@pytest.mark.django_db
def test_hide_exams_and_collections(client, create_users):
    from django.contrib.contenttypes.models import ContentType
    from physics.models import Exam as PhysicsExam
    from atlas.models import CaseCollection
    from generic.models import UserHiddenItem

    user1, user2 = create_users

    # 1. Create a PhysicsExam and associate with user1
    exam = PhysicsExam.objects.create(
        name="Test Physics Exam", exam_mode=True, active=True
    )
    user1.user_physics_exams.add(exam)

    # 2. Create a CaseCollection and associate with user1
    collection = CaseCollection.objects.create(
        name="Test Case Collection", exam_mode=True, active=True
    )
    user1.user_casecollection_exams.add(collection)

    # Log in as user1
    client.force_login(user1)

    # Verify they are visible on user scores page initially
    response = client.get(reverse("user_scores"))
    assert response.status_code == 200
    assert b"Test Physics Exam" in response.content

    # Load atlas collections partial
    response_cc = client.get(reverse("atlas:user_collections"))
    assert response_cc.status_code == 200
    assert b"Test Case Collection" in response_cc.content

    # 3. Hide the PhysicsExam
    exam_ct = ContentType.objects.get_for_model(PhysicsExam)
    response_hide_exam = client.post(
        reverse("generic:toggle_hide_item"),
        {"content_type_id": exam_ct.id, "object_id": exam.id},
    )
    assert response_hide_exam.status_code == 200
    assert response_hide_exam.headers.get("HX-Refresh") == "true"

    # Verify UserHiddenItem exists
    assert UserHiddenItem.objects.filter(
        user=user1, content_type=exam_ct, object_id=exam.id
    ).exists()

    # Verify exam is hidden now from standard user_scores
    response_hidden = client.get(reverse("user_scores"))
    assert response_hidden.status_code == 200
    assert b"Test Physics Exam" not in response_hidden.content

    # But present when show_hidden=true is requested
    response_show_hidden = client.get(reverse("user_scores") + "?show_hidden=true")
    assert response_show_hidden.status_code == 200
    assert b"Test Physics Exam" in response_show_hidden.content
    assert b"Hidden" in response_show_hidden.content

    # 4. Hide the CaseCollection
    cc_ct = ContentType.objects.get_for_model(CaseCollection)
    response_hide_cc = client.post(
        reverse("generic:toggle_hide_item"),
        {"content_type_id": cc_ct.id, "object_id": collection.pk},
    )
    assert response_hide_cc.status_code == 200
    assert response_hide_cc.headers.get("HX-Refresh") == "true"

    assert UserHiddenItem.objects.filter(
        user=user1, content_type=cc_ct, object_id=collection.pk
    ).exists()

    # Verify collection is hidden from normal view
    response_cc_hidden = client.get(reverse("atlas:user_collections"))
    assert response_cc_hidden.status_code == 200
    assert b"Test Case Collection" not in response_cc_hidden.content

    # Verify collection is visible in show_hidden view
    response_cc_show_hidden = client.get(reverse("atlas:user_collections") + "?show_hidden=true")
    assert response_cc_show_hidden.status_code == 200
    assert b"Test Case Collection" in response_cc_show_hidden.content
    assert b"Hidden" in response_cc_show_hidden.content

    # 5. Unhide the PhysicsExam by toggling again
    response_unhide_exam = client.post(
        reverse("generic:toggle_hide_item"),
        {"content_type_id": exam_ct.id, "object_id": exam.id},
    )
    assert response_unhide_exam.status_code == 200
    assert not UserHiddenItem.objects.filter(
        user=user1, content_type=exam_ct, object_id=exam.id
    ).exists()

    response_unhidden = client.get(reverse("user_scores"))
    assert response_unhidden.status_code == 200
    assert b"Test Physics Exam" in response_unhidden.content


@pytest.mark.django_db
def test_create_user_with_linked_supervisor(client, create_cid_manager, django_user_model):
    from generic.models import Supervisor

    client.force_login(create_cid_manager)

    # 1. Create a user WITH create_supervisor checked
    response = client.post(
        reverse("create_user"),
        {
            "username": "newsupervisor@example.com",
            "first_name": "New",
            "last_name": "Supervisor",
            "grade": "",
            "create_supervisor": True,
        },
    )

    assert response.status_code == 302
    assert response.url == reverse("accounts_list")

    # Verify user created
    new_user = django_user_model.objects.get(username="newsupervisor@example.com")
    assert new_user.first_name == "New"
    assert new_user.last_name == "Supervisor"

    # Verify linked supervisor created
    supervisor = Supervisor.objects.get(email="newsupervisor@example.com")
    assert supervisor.name == "New Supervisor"
    assert supervisor.user == new_user

    # 2. Create a user WITHOUT create_supervisor checked
    response_no_sv = client.post(
        reverse("create_user"),
        {
            "username": "regularuser@example.com",
            "first_name": "Regular",
            "last_name": "User",
            "grade": "",
            "create_supervisor": False,
        },
    )

    assert response_no_sv.status_code == 302

    # Verify user created
    regular_user = django_user_model.objects.get(username="regularuser@example.com")
    # Verify no supervisor was created
    assert not Supervisor.objects.filter(email="regularuser@example.com").exists()





