import pytest
from django.urls import reverse
from django.contrib.auth.models import Group, User
from django.test import Client
from generic.models import Examination, Supervisor, CidUserGroup, UserUserGroup, CidUser

SAFE_STATUS_CODES = {200, 301, 302, 303, 403, 404, 405}

@pytest.fixture
def candidate_client(db):
    user = User.objects.create_user(username="candidate", password="password", email="candidate@test.com")
    client = Client()
    client.force_login(user)
    return client

@pytest.fixture
def cid_manager_client(db):
    user = User.objects.create_user(username="manager", password="password", email="manager@test.com")
    g, _ = Group.objects.get_or_create(name="cid_user_manager")
    user.groups.add(g)
    client = Client()
    client.force_login(user)
    return client

@pytest.fixture
def superuser_client(db):
    user = User.objects.create_superuser(username="superuser", password="password", email="superuser@test.com")
    g, _ = Group.objects.get_or_create(name="cid_user_manager")
    user.groups.add(g)
    client = Client()
    client.force_login(user)
    return client

@pytest.fixture
def anonymous_client(db):
    return Client()

@pytest.fixture
def smoke_data(db, create_user, create_examination):
    supervisor = Supervisor.objects.create(
        email="sup@test.com",
        name="Test Supervisor",
        user=create_user
    )
    cid_group = CidUserGroup.objects.create(name="Cid Group")
    user_group = UserUserGroup.objects.create(name="User Group")
    cid_user = CidUser.objects.create(cid=99999, name="Test Cid User", email="cid@test.com", passcode="smokepasscode")

    return {
        "examination": create_examination,
        "supervisor": supervisor,
        "cid_group": cid_group,
        "user_group": user_group,
        "cid_user": cid_user,
    }

URL_SPECS = [
    ("generic:examination_view", None),
    ("generic:create_examination", None),
    ("generic:examination_detail", lambda d: {"pk": d["examination"].pk}),
    ("generic:examination_update", lambda d: {"pk": d["examination"].pk}),
    ("generic:examination_delete", lambda d: {"pk": d["examination"].pk}),
    ("generic:supervisor", None),
    ("generic:supervisor_create", None),
    ("generic:supervisor_detail", lambda d: {"pk": d["supervisor"].pk}),
    ("generic:supervisor_edit", lambda d: {"pk": d["supervisor"].pk}),
    ("generic:supervisor_delete", lambda d: {"pk": d["supervisor"].pk}),
    ("generic:supervisor_overview", lambda d: {"pk": d["supervisor"].pk}),
    ("generic:manage_cids", None),
    ("generic:manage_cid_exams", None),
    ("generic:cid_group_view", None),
    ("generic:cid_group_create", None),
    ("generic:cid_group_detail", lambda d: {"group_id": d["cid_group"].pk}),
    ("generic:cid_group_update", lambda d: {"pk": d["cid_group"].pk}),
    ("generic:cid_group_delete", lambda d: {"pk": d["cid_group"].pk}),
    ("generic:user_group_view", None),
    ("generic:user_group_create", None),
    ("generic:user_group_detail", lambda d: {"group_id": d["user_group"].pk}),
    ("generic:user_group_update", lambda d: {"pk": d["user_group"].pk}),
    ("generic:user_group_delete", lambda d: {"pk": d["user_group"].pk}),
]

@pytest.mark.django_db
@pytest.mark.parametrize("url_name, kwargs_lambda", URL_SPECS)
def test_url_smoke_anonymous(anonymous_client, smoke_data, url_name, kwargs_lambda):
    kwargs = kwargs_lambda(smoke_data) if kwargs_lambda else None
    url = reverse(url_name, kwargs=kwargs)
    response = anonymous_client.get(url, follow=False)
    assert response.status_code != 500, f"500 Server Error on {url_name} ({url}) for Anonymous"
    assert response.status_code in {200, 302, 403, 404}, f"Unexpected status code {response.status_code} on {url_name} for Anonymous"

@pytest.mark.django_db
@pytest.mark.parametrize("url_name, kwargs_lambda", URL_SPECS)
def test_url_smoke_candidate(candidate_client, smoke_data, url_name, kwargs_lambda):
    kwargs = kwargs_lambda(smoke_data) if kwargs_lambda else None
    url = reverse(url_name, kwargs=kwargs)
    response = candidate_client.get(url, follow=False)
    assert response.status_code != 500, f"500 Server Error on {url_name} ({url}) for Candidate"
    assert response.status_code in SAFE_STATUS_CODES, f"Unexpected status code {response.status_code} on {url_name} for Candidate"

@pytest.mark.django_db
@pytest.mark.parametrize("url_name, kwargs_lambda", URL_SPECS)
def test_url_smoke_manager(cid_manager_client, smoke_data, url_name, kwargs_lambda):
    kwargs = kwargs_lambda(smoke_data) if kwargs_lambda else None
    url = reverse(url_name, kwargs=kwargs)
    response = cid_manager_client.get(url, follow=False)
    assert response.status_code != 500, f"500 Server Error on {url_name} ({url}) for Manager"
    assert response.status_code in SAFE_STATUS_CODES, f"Unexpected status code {response.status_code} on {url_name} for Manager"

@pytest.mark.django_db
@pytest.mark.parametrize("url_name, kwargs_lambda", URL_SPECS)
def test_url_smoke_superuser(superuser_client, smoke_data, url_name, kwargs_lambda):
    kwargs = kwargs_lambda(smoke_data) if kwargs_lambda else None
    url = reverse(url_name, kwargs=kwargs)
    response = superuser_client.get(url, follow=False)
    assert response.status_code != 500, f"500 Server Error on {url_name} ({url}) for Superuser"
    assert response.status_code in {200, 301, 302, 303}, f"Unexpected status code {response.status_code} on {url_name} for Superuser"
