from typing import Callable
import pytest
from django.test import Client
from django.urls import reverse
from hypothesis import given, strategies as st
from logot import Logot, logged

from .models import Teil

names = st.text(alphabet=st.characters(exclude_categories=["C"]), min_size=1)


@given(data=names)
def test_submitted_data_ends_up_in_database(data, session: Client):
    with pytest.raises(Teil.DoesNotExist):
        Teil.objects.get(name=data)

    response = session.post(reverse("collector:enter"), data={"new_name": data})
    assert response.status_code == 302
    assert Teil.objects.get(name=data)


@given(data=names)
def test_entering_same_name_twice_does_not_change_database_entry(data, session: Client):
    assert not Teil.objects.filter(name=data).exists()

    response = session.post(reverse("collector:enter"), data={"new_name": data})
    assert response.status_code == 302
    assert Teil.objects.filter(name=data).count() == 1

    response = session.post(reverse("collector:enter"), data={"new_name": data})
    assert response.status_code == 302
    assert Teil.objects.filter(name=data).count() == 1


@pytest.mark.parametrize(
    "http_method,expected_status",
    [
        ("GET", 405),
        ("PATCH", 405),
        ("POST", 302),
        ("PUT", 405),
    ],
)
def test_enter_endpoint_accepts_only_post_requests(
    client: Client,
    http_method: str,
    expected_status: int,
    random_name: Callable[[int], str],
):
    client_method = getattr(client, http_method.lower())

    response = client_method(
        reverse("collector:enter"), data={"new_name": random_name(8)}
    )
    assert response.status_code == expected_status


@pytest.mark.xfail(reason="WIP, kein Plan warum nichts gecaptured zu werden scheint")
def test_enter_endpoints_emits_expected_logs(
    client: Client, logot: Logot, random_name: Callable[[int], str]
):
    same_name = random_name(10)

    response = client.post(reverse("collector:enter"), data={"new_name": same_name})
    assert response.status_code == 302
    logot.assert_logged(logged.info("New Teil entered"))

    response = client.post(reverse("collector:enter"), data={"new_name": same_name})
    assert response.status_code == 302
    logot.assert_logged(logged.warning("Teil already existed"))