import pytest
from django.urls import reverse
from django.contrib.auth.models import User
from django.test import Client
from django.contrib.contenttypes.models import ContentType
from generic.models import Supervisor, UserProfile, CidUserExam
from physics.models import Exam as PhysicsExam
from atlas.models import CaseCollection, Case, CaseDetail

@pytest.fixture
def supervisor_user(db):
    return User.objects.create_user(username="supervisor_user", password="password", email="supervisor@test.com")

@pytest.fixture
def supervisor_model(db, supervisor_user):
    return Supervisor.objects.create(name="Test Supervisor", email="supervisor@test.com", user=supervisor_user)

@pytest.fixture
def other_supervisor_user(db):
    return User.objects.create_user(username="other_supervisor", password="password", email="other@test.com")

@pytest.fixture
def other_supervisor(db, other_supervisor_user):
    return Supervisor.objects.create(name="Other Supervisor", email="other@test.com", user=other_supervisor_user)

@pytest.fixture
def trainee_user(db, supervisor_model):
    user = User.objects.create_user(username="trainee", password="password", email="trainee@test.com")
    profile = user.userprofile
    profile.supervisor = supervisor_model
    profile.save()
    return user

@pytest.fixture
def physics_exam(db):
    return PhysicsExam.objects.create(name="Test Physics Exam", exam_mode=True)

@pytest.fixture
def case_collection(db):
    collection = CaseCollection.objects.create(name="Test Case Collection")
    # Enable messaging
    collection.feedback_once_collection_complete = True
    collection.save()
    return collection

@pytest.fixture
def case_model(db):
    return Case.objects.create(title="Test Case")

@pytest.fixture
def case_detail(db, case_collection, case_model):
    return CaseDetail.objects.create(collection=case_collection, case=case_model, sort_order=0)

@pytest.fixture
def exam_attempt(db, trainee_user, physics_exam):
    ct = ContentType.objects.get_for_model(PhysicsExam)
    return CidUserExam.objects.create(
        content_type=ct,
        object_id=physics_exam.pk,
        user_user=trainee_user,
        share_with_supervisor=False
    )

@pytest.fixture
def collection_attempt(db, trainee_user, case_collection):
    ct = ContentType.objects.get_for_model(CaseCollection)
    return CidUserExam.objects.create(
        content_type=ct,
        object_id=case_collection.pk,
        user_user=trainee_user,
        share_with_supervisor=False
    )

@pytest.mark.django_db
def test_supervisor_overview_access(
    supervisor_user,
    other_supervisor_user,
    supervisor_model,
    trainee_user,
    physics_exam,
    exam_attempt,
    case_collection,
    collection_attempt,
):
    client = Client()
    url = reverse("generic:supervisor_overview", kwargs={"pk": supervisor_model.pk})
    
    # Anonymous blocked
    response = client.get(url)
    assert response.status_code == 302
    
    # Other supervisor blocked
    client.force_login(other_supervisor_user)
    response = client.get(url)
    assert response.status_code == 403
    
    # Authorized supervisor allowed
    client.force_login(supervisor_user)
    
    # First test with private attempts
    response = client.get(url)
    assert response.status_code == 200
    assert b"Test Physics Exam" in response.content
    assert b"Test Case Collection" in response.content
    assert b"Locked" in response.content
    
    # Now test with shared attempts
    exam_attempt.share_with_supervisor = True
    exam_attempt.save()
    collection_attempt.share_with_supervisor = True
    collection_attempt.save()
    
    response = client.get(url)
    assert response.status_code == 200
    assert b"Test Physics Exam" in response.content
    assert b"Test Case Collection" in response.content
    assert b"0 / 0 cases" in response.content  # collection progress check

@pytest.mark.django_db
def test_supervisor_trainee_access(supervisor_user, other_supervisor_user, supervisor_model, trainee_user):
    client = Client()
    url = reverse("generic:supervisor_trainee", kwargs={"pk": supervisor_model.pk, "trainee_id": trainee_user.pk})
    
    # Other supervisor blocked
    client.force_login(other_supervisor_user)
    response = client.get(url)
    assert response.status_code == 403
    
    # Authorized supervisor allowed
    client.force_login(supervisor_user)
    response = client.get(url)
    assert response.status_code == 200

@pytest.mark.django_db
def test_exam_scores_user_supervisor_sharing(supervisor_user, other_supervisor_user, supervisor_model, trainee_user, physics_exam, exam_attempt):
    client = Client()
    url = reverse("physics:exam_scores_user_supervisor", kwargs={"pk": physics_exam.pk, "user_id": trainee_user.pk})
    
    # Other supervisor blocked regardless
    client.force_login(other_supervisor_user)
    response = client.get(url)
    assert response.status_code == 403
    
    # Authorized supervisor blocked when not shared
    client.force_login(supervisor_user)
    response = client.get(url)
    assert response.status_code == 403
    
    # Share and check allowed
    exam_attempt.share_with_supervisor = True
    exam_attempt.save()
    response = client.get(url)
    assert response.status_code == 200
    
    # Unshare, but set results_supervisor_visible = True
    exam_attempt.share_with_supervisor = False
    exam_attempt.save()
    physics_exam.results_supervisor_visible = True
    physics_exam.save()
    response = client.get(url)
    assert response.status_code == 200

@pytest.mark.django_db
def test_supervisor_collection_feedback_sharing(supervisor_user, supervisor_model, trainee_user, case_collection, collection_attempt):
    client = Client()
    url = reverse("atlas:collection_history_user", kwargs={
        "exam_id": case_collection.pk,
        "user_pk": trainee_user.pk
    })
    
    # Supervisor blocked when not shared
    client.force_login(supervisor_user)
    response = client.get(url)
    assert response.status_code == 403
    
    # Supervisor allowed when shared
    collection_attempt.share_with_supervisor = True
    collection_attempt.save()
    response = client.get(url)
    assert response.status_code == 200

@pytest.mark.django_db
def test_collection_case_review_thread_supervisor_access(supervisor_user, supervisor_model, trainee_user, case_collection, collection_attempt, case_model, case_detail):
    client = Client()
    url = reverse("atlas:collection_case_review_thread", kwargs={
        "exam_id": case_collection.pk,
        "cid_user_exam_id": collection_attempt.pk,
        "case_id": case_model.pk
    })
    
    # Blocked when attempt is not shared
    client.force_login(supervisor_user)
    response = client.get(url)
    assert response.status_code == 403
    
    # Allowed when shared
    collection_attempt.share_with_supervisor = True
    collection_attempt.save()
    response = client.get(url)
    assert response.status_code == 200
    
    # Post a feedback reply as supervisor
    response = client.post(url, {
        "action": "add_message",
        "message_type": "FB",
        "message": "Nice job trainee!"
    }, HTTP_X_REQUESTED_WITH="XMLHttpRequest")
    assert response.status_code == 200
    assert b"Nice job trainee!" in response.content


@pytest.mark.django_db
def test_collection_case_view_take_review_user(
    supervisor_user,
    other_supervisor_user,
    supervisor_model,
    trainee_user,
    case_collection,
    case_model,
    case_detail,
    collection_attempt,
):
    """Supervisors can view a trainee's case answers in read-only mode.

    - Unshared attempts are blocked (403).
    - Shared attempts return 200 with the reviewer banner present.
    - Other supervisors with no relationship to the trainee are blocked.
    - A collection author can always view the answers.
    """
    client = Client()
    url = reverse(
        "atlas:collection_case_view_take_review_user",
        kwargs={"pk": case_collection.pk, "case_number": 0, "user_pk": trainee_user.pk},
    )

    # Anonymous user is redirected to login
    response = client.get(url)
    assert response.status_code == 302

    # Authorized supervisor blocked when attempt not shared
    client.force_login(supervisor_user)
    response = client.get(url)
    assert response.status_code == 403

    # Authorized supervisor allowed when attempt is shared
    collection_attempt.share_with_supervisor = True
    collection_attempt.save()
    response = client.get(url)
    assert response.status_code == 200
    # Reviewer banner should be present
    assert b"Reviewer mode" in response.content
    assert b"read-only" in response.content

    # Other supervisor (not linked to trainee) is still blocked even when shared
    client.force_login(other_supervisor_user)
    response = client.get(url)
    assert response.status_code == 403

    # Collection author can always view the answers
    author_user = User.objects.create_user(username="author_user", password="password")
    case_collection.author.add(author_user)
    client.force_login(author_user)
    response = client.get(url)
    assert response.status_code == 200
    assert b"Reviewer mode" in response.content
