# retoor <retoor@molodetz.nl>

import time

import pytest
import requests

from devplacepy.database import REPORTABLE_TARGETS, get_table, refresh_snapshot
from tests.conftest import BASE_URL

JSON = {"Accept": "application/json"}
_counter = [0]


def _unique(prefix="rep"):
    _counter[0] += 1
    return f"{prefix}{int(time.time() * 1000)}{_counter[0]}"


def _member():
    name = _unique()
    session = requests.Session()
    session.post(
        f"{BASE_URL}/auth/signup",
        data={
            "username": name,
            "email": f"{name}@t.dev",
            "password": "secret123",
            "confirm_password": "secret123",
            "birth_date": "1990-01-01",
            "accept_terms": "1",
        },
        allow_redirects=True,
    )
    return session, name


def _post(session, title="Reportable post"):
    response = session.post(
        f"{BASE_URL}/posts/create",
        data={"title": title, "content": "A post that can be reported by others."},
        headers=JSON,
    )
    assert response.status_code == 200, response.text
    return response.json()["data"]["uid"]


def _reports_for(target_type, target_uid):
    refresh_snapshot()
    return list(
        get_table("content_reports").find(
            target_type=target_type, target_uid=target_uid, deleted_at=None
        )
    )


@pytest.fixture(scope="module")
def author(app_server):
    return _member()


def test_report_reasons_are_public(app_server):
    response = requests.get(f"{BASE_URL}/reports/reasons", headers=JSON)
    assert response.status_code == 200
    keys = {entry["key"] for entry in response.json()["reasons"]}
    assert {"harassment", "spam", "intellectual_property", "other"} <= keys


def test_the_reasons_page_is_indexable_with_breadcrumbs_and_a_site_schema(app_server):
    html = requests.get(f"{BASE_URL}/reports/reasons").text
    assert '<meta name="robots" content="index,follow">' in html
    assert 'class="breadcrumb"' in html
    assert "Report reasons" in html
    assert '"@type": "WebSite"' in html


def test_the_reasons_page_title_carries_one_site_suffix(app_server):
    html = requests.get(f"{BASE_URL}/reports/reasons").text
    start = html.index("<title>") + len("<title>")
    title = html[start : html.index("</title>", start)]
    assert title == "Report reasons - DevPlace", title


def test_reporting_a_post_records_one_row_with_the_owner(author):
    author_session, author_name = author
    post_uid = _post(author_session)
    reporter, _ = _member()
    response = reporter.post(
        f"{BASE_URL}/reports/post/{post_uid}",
        data={"reason": "harassment", "detail": "Targets me personally."},
        headers=JSON,
    )
    assert response.status_code == 200, response.text
    assert response.json()["data"]["status"] == "open"
    rows = _reports_for("post", post_uid)
    assert len(rows) == 1
    owner = get_table("users").find_one(username=author_name)
    assert rows[0]["owner_uid"] == owner["uid"]
    assert rows[0]["origin"] == "member"


def test_second_report_by_the_same_reporter_updates_instead_of_duplicating(author):
    author_session, _ = author
    post_uid = _post(author_session)
    reporter, _ = _member()
    for reason in ("spam", "harassment"):
        response = reporter.post(
            f"{BASE_URL}/reports/post/{post_uid}",
            data={"reason": reason},
            headers=JSON,
        )
        assert response.status_code == 200
    rows = _reports_for("post", post_uid)
    assert len(rows) == 1
    assert rows[0]["reason"] == "harassment"


def test_a_different_reporter_creates_a_second_row(author):
    author_session, _ = author
    post_uid = _post(author_session)
    for _ in range(2):
        reporter, _ = _member()
        reporter.post(
            f"{BASE_URL}/reports/post/{post_uid}",
            data={"reason": "spam"},
            headers=JSON,
        )
    assert len(_reports_for("post", post_uid)) == 2


def test_guests_cannot_report(author):
    author_session, _ = author
    post_uid = _post(author_session)
    response = requests.post(
        f"{BASE_URL}/reports/post/{post_uid}",
        data={"reason": "spam"},
        headers=JSON,
    )
    assert response.status_code == 401


def test_reporting_your_own_content_is_refused(author):
    author_session, _ = author
    post_uid = _post(author_session)
    response = author_session.post(
        f"{BASE_URL}/reports/post/{post_uid}",
        data={"reason": "spam"},
        headers=JSON,
    )
    assert response.status_code == 400
    assert _reports_for("post", post_uid) == []


def test_an_unknown_target_type_is_refused(author):
    reporter, _ = _member()
    response = reporter.post(
        f"{BASE_URL}/reports/not_a_surface/whatever",
        data={"reason": "spam"},
        headers=JSON,
    )
    assert response.status_code == 400


def test_an_unknown_reason_is_refused(author):
    author_session, _ = author
    post_uid = _post(author_session)
    reporter, _ = _member()
    response = reporter.post(
        f"{BASE_URL}/reports/post/{post_uid}",
        data={"reason": "not_a_reason"},
        headers=JSON,
    )
    assert response.status_code == 422


def test_every_registry_target_type_is_accepted_by_the_route(author):
    author_session, _ = author
    reporter, _ = _member()
    for target_type in REPORTABLE_TARGETS:
        response = reporter.post(
            f"{BASE_URL}/reports/{target_type}/missing-{target_type}",
            data={"reason": "other"},
            headers=JSON,
        )
        assert response.status_code == 200, f"{target_type}: {response.text}"


def test_the_report_dialog_uses_the_shared_auth_field_classes(app_server):
    reporter, _ = _member()
    html = reporter.get(f"{BASE_URL}/feed").text
    assert 'id="report-dialog"' in html
    assert 'class="auth-field auth-field-gap"' in html
    assert 'class="modal-footer"' in html
    assert 'id="report-reason"' in html
    assert 'id="report-detail"' in html


def test_your_reports_page_loads_the_admin_table_styles(author):
    author_session, _ = author
    post_uid = _post(author_session)
    reporter, _ = _member()
    reporter.post(
        f"{BASE_URL}/reports/post/{post_uid}", data={"reason": "spam"}, headers=JSON
    )
    html = reporter.get(f"{BASE_URL}/reports/mine").text
    assert "css/admin.css" in html
    assert 'class="admin-table"' in html


def test_mine_lists_only_your_own_reports(author):
    author_session, _ = author
    post_uid = _post(author_session)
    mine, _ = _member()
    other, _ = _member()
    mine.post(
        f"{BASE_URL}/reports/post/{post_uid}", data={"reason": "spam"}, headers=JSON
    )
    other.post(
        f"{BASE_URL}/reports/post/{post_uid}",
        data={"reason": "harassment"},
        headers=JSON,
    )
    payload = mine.get(f"{BASE_URL}/reports/mine", headers=JSON).json()
    reasons = {entry["reason"] for entry in payload["reports"]}
    assert "spam" in reasons
    assert "harassment" not in reasons
    assert requests.get(f"{BASE_URL}/reports/mine", headers=JSON).status_code == 401
