# retoor <retoor@molodetz.nl>

import time

import pytest
import requests

from devplacepy.database import get_table, refresh_snapshot, set_setting
from devplacepy.schemas import AdminUserOut
from tests.conftest import BASE_URL

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


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


def _member(prefix="mod"):
    name = _unique(prefix)
    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 _admin_session(app_server):
    session = requests.Session()
    session.post(
        f"{BASE_URL}/auth/login",
        data={"email": "alice@test.devplace", "password": "secret123"},
        allow_redirects=True,
    )
    return session


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


def _report(target_uid, reason="harassment"):
    reporter, name = _member("rep")
    response = reporter.post(
        f"{BASE_URL}/reports/post/{target_uid}",
        data={"reason": reason, "detail": "Queue test"},
        headers=JSON,
    )
    assert response.status_code == 200, response.text
    refresh_snapshot()
    row = get_table("content_reports").find_one(
        target_uid=target_uid, deleted_at=None, order_by=["-created_at"]
    )
    return row["uid"], reporter, name


@pytest.fixture(scope="module")
def admin(app_server, seeded_db):
    set_setting("moderation_sla_hours", "24")
    return _admin_session(app_server)


def test_queue_is_admin_only(admin, app_server):
    member, _ = _member()
    assert member.get(f"{BASE_URL}/admin/moderation", headers=JSON).status_code == 403
    assert requests.get(f"{BASE_URL}/admin/moderation", headers=JSON).status_code == 401
    assert admin.get(f"{BASE_URL}/admin/moderation", headers=JSON).status_code == 200


def test_queue_reports_the_sla_snapshot(admin):
    payload = admin.get(f"{BASE_URL}/admin/moderation", headers=JSON).json()
    assert payload["sla"]["sla_hours"] == 24
    assert "oldest_open_hours" in payload["sla"]
    assert set(payload["counts"]) == {"open", "acknowledged", "actioned", "dismissed"}


def test_queue_is_ordered_oldest_open_first(admin):
    author, _ = _member("auth")
    first = _report(_post(author))[0]
    second = _report(_post(author))[0]
    payload = admin.get(f"{BASE_URL}/admin/moderation?status=open", headers=JSON).json()
    order = [entry["uid"] for entry in payload["reports"]]
    assert order.index(first) < order.index(second)


def test_the_report_detail_projects_the_subject_through_the_admin_user_model(admin):
    author, author_name = _member("subj")
    report_uid = _report(_post(author))[0]
    payload = admin.get(
        f"{BASE_URL}/admin/moderation/{report_uid}", headers=JSON
    ).json()
    subject = payload["subject"]
    assert subject["username"] == author_name
    assert "password_hash" not in subject
    assert "api_key" not in subject
    assert set(subject) <= set(AdminUserOut.model_fields)


def test_acknowledging_keeps_the_report_open_for_a_decision(admin):
    author, _ = _member("auth")
    report_uid = _report(_post(author))[0]
    response = admin.post(
        f"{BASE_URL}/admin/moderation/{report_uid}/status",
        data={"status": "acknowledged"},
        headers=JSON,
    )
    assert response.status_code == 200
    refresh_snapshot()
    assert (
        get_table("content_reports").find_one(uid=report_uid)["status"]
        == "acknowledged"
    )


def test_a_decision_records_an_action_row_and_resolves_the_report(admin):
    author, _ = _member("auth")
    post_uid = _post(author)
    report_uid, _, _ = _report(post_uid)
    response = admin.post(
        f"{BASE_URL}/admin/moderation/{report_uid}/decide",
        data={"action": "remove_content", "reason": "Breaks the guidelines"},
        headers=JSON,
    )
    assert response.status_code == 200, response.text
    refresh_snapshot()
    report = get_table("content_reports").find_one(uid=report_uid)
    assert report["status"] == "actioned"
    assert report["resolved_at"]
    action = get_table("moderation_actions").find_one(report_uid=report_uid)
    assert action["action"] == "remove_content"
    assert action["reason"] == "Breaks the guidelines"
    assert get_table("posts").find_one(uid=post_uid)["deleted_at"]


def test_deciding_a_resolved_report_twice_is_refused(admin):
    author, _ = _member("auth")
    report_uid = _report(_post(author))[0]
    first = admin.post(
        f"{BASE_URL}/admin/moderation/{report_uid}/decide",
        data={"action": "dismiss"},
        headers=JSON,
    )
    assert first.status_code == 200
    second = admin.post(
        f"{BASE_URL}/admin/moderation/{report_uid}/decide",
        data={"action": "dismiss"},
        headers=JSON,
    )
    assert second.status_code == 409


def test_escalating_raises_severity_and_keeps_the_report_open(admin):
    author, _ = _member("auth")
    report_uid = _report(_post(author), reason="spam")[0]
    response = admin.post(
        f"{BASE_URL}/admin/moderation/{report_uid}/decide",
        data={"action": "escalate"},
        headers=JSON,
    )
    assert response.status_code == 200
    refresh_snapshot()
    report = get_table("content_reports").find_one(uid=report_uid)
    assert report["severity"] == "critical"
    assert report["status"] == "acknowledged"


def test_suspension_blocks_writing_but_not_reading_or_deleting(admin):
    author, author_name = _member("susp")
    refresh_snapshot()
    subject = get_table("users").find_one(username=author_name)
    response = admin.post(
        f"{BASE_URL}/admin/users/{subject['uid']}/suspend",
        data={"reason": "Testing enforcement", "duration_hours": "24"},
        headers=JSON,
    )
    assert response.status_code == 200, response.text
    try:
        blocked = author.post(
            f"{BASE_URL}/posts/create",
            data={"title": "nope", "content": "This must not be published at all."},
            headers=JSON,
        )
        assert blocked.status_code == 403
        assert author.get(f"{BASE_URL}/feed", headers=JSON).status_code == 200
        assert (
            author.get(f"{BASE_URL}/profile/{author_name}/delete", headers=JSON).status_code
            == 200
        )
    finally:
        admin.post(f"{BASE_URL}/admin/users/{subject['uid']}/lift", headers=JSON)
    allowed = author.post(
        f"{BASE_URL}/posts/create",
        data={"title": "back", "content": "Writing works again after the lift."},
        headers=JSON,
    )
    assert allowed.status_code == 200


def test_ban_revokes_every_session(admin):
    victim, victim_name = _member("ban")
    refresh_snapshot()
    subject = get_table("users").find_one(username=victim_name)
    response = admin.post(
        f"{BASE_URL}/admin/users/{subject['uid']}/ban",
        data={"reason": "Testing the ban path"},
        headers=JSON,
    )
    assert response.status_code == 200
    refresh_snapshot()
    assert get_table("users").find_one(uid=subject["uid"])["is_active"] in (0, False)
    assert victim.get(f"{BASE_URL}/reports/mine", headers=JSON).status_code == 401


def test_an_admin_cannot_enforce_against_themselves(admin, seeded_db):
    refresh_snapshot()
    alice = get_table("users").find_one(username="alice_test")
    response = admin.post(
        f"{BASE_URL}/admin/users/{alice['uid']}/suspend",
        data={"reason": "self", "duration_hours": "1"},
        headers=JSON,
    )
    assert response.status_code == 200
    refresh_snapshot()
    assert not (get_table("users").find_one(uid=alice["uid"])["suspended_until"] or "")
