import time
import uuid
import pytest
import requests
from conftest import API, auth_headers


# ---------------- Health & Auth ----------------
class TestAuth:
    def test_root(self):
        r = requests.get(f"{API}/")
        assert r.status_code == 200
        assert r.json()["message"] == "QRCupid API"

    def test_admin_login(self):
        r = requests.post(f"{API}/auth/login", json={"email": "admin@qrcupid.com", "password": "Admin@123"})
        assert r.status_code == 200
        data = r.json()
        assert data["role"] == "admin"
        assert "token" in data
        assert data["email"] == "admin@qrcupid.com"

    def test_register_and_me(self):
        email = f"test_{uuid.uuid4().hex[:8]}@test.com"
        r = requests.post(f"{API}/auth/register", json={"email": email, "password": "Test@1234", "name": "TestUser"})
        assert r.status_code == 200, r.text
        d = r.json()
        assert d["email"] == email.lower()
        assert "token" in d
        assert "verify_token" in d  # mocked email
        token = d["token"]
        me = requests.get(f"{API}/auth/me", headers=auth_headers(token))
        assert me.status_code == 200
        assert me.json()["email"] == email

    def test_login_invalid(self):
        r = requests.post(f"{API}/auth/login", json={"email": "nobody@x.com", "password": "wrong"})
        assert r.status_code == 401

    def test_verify_email(self):
        email = f"TEST_{uuid.uuid4().hex[:8]}@v.com"
        r = requests.post(f"{API}/auth/register", json={"email": email, "password": "Test@1234", "name": "Verif"})
        d = r.json()
        token, vtoken = d["token"], d["verify_token"]
        r2 = requests.post(f"{API}/auth/verify-email", json={"token": vtoken}, headers=auth_headers(token))
        assert r2.status_code == 200
        assert r2.json()["email_verified"] is True
        me = requests.get(f"{API}/auth/me", headers=auth_headers(token)).json()
        assert me["email_verified"] is True
        assert me["verified_badge"] is True

    def test_forgot_and_reset(self):
        email = f"TEST_{uuid.uuid4().hex[:8]}@r.com"
        requests.post(f"{API}/auth/register", json={"email": email, "password": "Test@1234", "name": "Reset"})
        r = requests.post(f"{API}/auth/forgot-password", json={"email": email})
        assert r.status_code == 200
        rtok = r.json()["reset_token"]
        assert rtok
        r2 = requests.post(f"{API}/auth/reset-password", json={"token": rtok, "password": "NewPass@123"})
        assert r2.status_code == 200
        # login with new password
        r3 = requests.post(f"{API}/auth/login", json={"email": email, "password": "NewPass@123"})
        assert r3.status_code == 200


# ---------------- Profile ----------------
class TestProfile:
    def test_update_profile_persistence(self, maya_token):
        token = maya_token["token"]
        new_bio = f"Updated bio {uuid.uuid4().hex[:6]}"
        r = requests.put(f"{API}/profile", headers=auth_headers(token),
                         json={"bio": new_bio, "age": 28, "gender": "Woman", "location": "Brooklyn",
                               "interests": ["Travel", "Coffee"]})
        assert r.status_code == 200
        assert r.json()["bio"] == new_bio
        # verify GET persists
        me = requests.get(f"{API}/auth/me", headers=auth_headers(token)).json()
        assert me["bio"] == new_bio
        assert me["age"] == 28
        assert me["location"] == "Brooklyn"

    def test_public_profile_by_slug(self, maya_token):
        token = maya_token["token"]
        me = requests.get(f"{API}/auth/me", headers=auth_headers(token)).json()
        slug = me["slug"]
        r = requests.get(f"{API}/profile/public/{slug}")
        assert r.status_code == 200
        assert r.json()["slug"] == slug
        assert "email" not in r.json() or r.json().get("email") is None


# ---------------- Discovery ----------------
class TestDiscovery:
    def test_browse_members(self, maya_token):
        token = maya_token["token"]
        r = requests.get(f"{API}/members", headers=auth_headers(token))
        assert r.status_code == 200
        ms = r.json()
        assert isinstance(ms, list)
        assert len(ms) >= 5
        # ensure no admin and no self
        for m in ms:
            assert m.get("role") != "admin"
            assert m["id"] != maya_token["id"]

    def test_browse_filter_gender(self, maya_token):
        token = maya_token["token"]
        r = requests.get(f"{API}/members?gender=Man", headers=auth_headers(token))
        assert r.status_code == 200
        for m in r.json():
            assert m.get("gender") == "Man"

    def test_browse_filter_age_range(self, maya_token):
        token = maya_token["token"]
        r = requests.get(f"{API}/members?min_age=28&max_age=35", headers=auth_headers(token))
        assert r.status_code == 200
        for m in r.json():
            if m.get("age") is not None:
                assert 28 <= m["age"] <= 35

    def test_search_keyword(self, maya_token):
        token = maya_token["token"]
        r = requests.get(f"{API}/members?q=coffee", headers=auth_headers(token))
        assert r.status_code == 200
        assert isinstance(r.json(), list)


# ---------------- Likes / Favorites / Matches ----------------
class TestSocial:
    def test_like_toggle(self, maya_token, jordan_token):
        tok = maya_token["token"]
        jid = jordan_token["id"]
        # ensure clean: toggle once
        r1 = requests.post(f"{API}/like/{jid}", headers=auth_headers(tok))
        assert r1.status_code == 200
        r2 = requests.post(f"{API}/like/{jid}", headers=auth_headers(tok))
        assert r2.status_code == 200
        # second call toggles back, so liked flips
        assert r1.json()["liked"] != r2.json()["liked"]

    def test_favorites_flow(self, maya_token, jordan_token):
        tok = maya_token["token"]
        jid = jordan_token["id"]
        # ensure favorited
        cur = requests.get(f"{API}/favorites", headers=auth_headers(tok)).json()
        if jid in [m["id"] for m in cur]:
            requests.post(f"{API}/favorite/{jid}", headers=auth_headers(tok))  # unfav
        r = requests.post(f"{API}/favorite/{jid}", headers=auth_headers(tok))
        assert r.status_code == 200
        assert r.json()["favorited"] is True
        favs = requests.get(f"{API}/favorites", headers=auth_headers(tok)).json()
        assert any(m["id"] == jid for m in favs)

    def test_matches_seeded(self, maya_token, jordan_token):
        # ensure mutual like exists: explicitly create mutual likes (idempotent set)
        tok_m = maya_token["token"]; tok_j = jordan_token["token"]
        mid = maya_token["id"]; jid = jordan_token["id"]
        # check current state and toggle until both like each other
        me = requests.get(f"{API}/auth/me", headers=auth_headers(tok_m)).json()
        if jid not in me.get("likes", []):
            requests.post(f"{API}/like/{jid}", headers=auth_headers(tok_m))
        jme = requests.get(f"{API}/auth/me", headers=auth_headers(tok_j)).json()
        if mid not in jme.get("likes", []):
            requests.post(f"{API}/like/{mid}", headers=auth_headers(tok_j))
        r = requests.get(f"{API}/matches", headers=auth_headers(tok_m))
        assert r.status_code == 200
        assert any(m["id"] == jid for m in r.json())

    def test_likes_received_premium_gate(self, maya_token):
        # ensure maya is free first (could be premium from prior test)
        requests.post(f"{API}/subscription/cancel", headers=auth_headers(maya_token["token"]))
        r = requests.get(f"{API}/likes/received", headers=auth_headers(maya_token["token"]))
        assert r.status_code == 402


# ---------------- Messaging ----------------
class TestMessaging:
    def test_send_and_read_messages(self, maya_token, jordan_token):
        tok_m = maya_token["token"]; tok_j = jordan_token["token"]
        jid = jordan_token["id"]
        r = requests.post(f"{API}/messages", headers=auth_headers(tok_m),
                          json={"to_user_id": jid, "text": "Hello Jordan TEST"})
        assert r.status_code == 200
        msg = r.json()
        assert msg["text"] == "Hello Jordan TEST"
        assert msg["read"] is False
        # jordan fetches conversation -> marks read
        conv = requests.get(f"{API}/messages/{maya_token['id']}", headers=auth_headers(tok_j))
        assert conv.status_code == 200
        # now maya fetches again, message should be read
        conv2 = requests.get(f"{API}/messages/{jid}", headers=auth_headers(tok_m)).json()
        sent_msg = next((m for m in conv2["messages"] if m["id"] == msg["id"]), None)
        assert sent_msg is not None
        assert sent_msg["read"] is True

    def test_threads_list(self, maya_token):
        r = requests.get(f"{API}/messages/threads", headers=auth_headers(maya_token["token"]))
        assert r.status_code == 200
        assert isinstance(r.json(), list)


# ---------------- Block / Report ----------------
class TestModeration:
    def test_report(self, maya_token, jordan_token):
        r = requests.post(f"{API}/report", headers=auth_headers(maya_token["token"]),
                          json={"reported_user_id": jordan_token["id"], "reason": "spam", "details": "TEST report"})
        assert r.status_code == 200
        assert r.json()["status"] == "open"

    def test_block_toggle(self, maya_token, jordan_token):
        r = requests.post(f"{API}/block/{jordan_token['id']}", headers=auth_headers(maya_token["token"]))
        assert r.status_code == 200
        # toggle back
        r2 = requests.post(f"{API}/block/{jordan_token['id']}", headers=auth_headers(maya_token["token"]))
        assert r2.status_code == 200
        assert r.json()["blocked"] != r2.json()["blocked"]


# ---------------- Payments ----------------
class TestPayments:
    def test_plans(self):
        r = requests.get(f"{API}/plans")
        assert r.status_code == 200
        ids = [p["id"] for p in r.json()]
        assert "premium_monthly" in ids and "premium_yearly" in ids

    def test_subscribe_and_history(self, maya_token):
        tok = maya_token["token"]
        r = requests.post(f"{API}/subscribe", headers=auth_headers(tok), json={
            "plan": "premium_monthly", "card_number": "4111 1111 1111 1111",
            "exp": "12/30", "cvv": "123", "name_on_card": "Maya Chen"
        })
        assert r.status_code == 200, r.text
        d = r.json()
        assert d["payment"]["status"] == "success"
        assert d["user"]["membership"] == "premium"
        # history
        h = requests.get(f"{API}/payments/history", headers=auth_headers(tok))
        assert h.status_code == 200
        assert len(h.json()) >= 1
        # premium gate now passes
        lr = requests.get(f"{API}/likes/received", headers=auth_headers(tok))
        assert lr.status_code == 200
        # cancel
        c = requests.post(f"{API}/subscription/cancel", headers=auth_headers(tok))
        assert c.status_code == 200
        assert c.json()["membership"] == "free"


# ---------------- Admin ----------------
class TestAdmin:
    def test_stats(self, admin_token):
        r = requests.get(f"{API}/admin/stats", headers=auth_headers(admin_token))
        assert r.status_code == 200
        d = r.json()
        for k in ["total_users", "premium_users", "free_users", "total_revenue", "revenue_series", "membership_distribution"]:
            assert k in d

    def test_admin_users_list(self, admin_token):
        r = requests.get(f"{API}/admin/users", headers=auth_headers(admin_token))
        assert r.status_code == 200
        assert len(r.json()) >= 5

    def test_admin_reports(self, admin_token):
        r = requests.get(f"{API}/admin/reports", headers=auth_headers(admin_token))
        assert r.status_code == 200
        assert isinstance(r.json(), list)

    def test_admin_payments(self, admin_token):
        r = requests.get(f"{API}/admin/payments", headers=auth_headers(admin_token))
        assert r.status_code == 200

    def test_admin_moderation(self, admin_token):
        r = requests.get(f"{API}/admin/moderation", headers=auth_headers(admin_token))
        assert r.status_code == 200
        assert isinstance(r.json(), list)

    def test_admin_suspend_verify(self, admin_token):
        # create a temp user
        email = f"TEST_{uuid.uuid4().hex[:8]}@adm.com"
        r = requests.post(f"{API}/auth/register", json={"email": email, "password": "Test@1234", "name": "Tmp"})
        uid = r.json()["id"]
        s = requests.post(f"{API}/admin/users/{uid}/suspend", headers=auth_headers(admin_token))
        assert s.status_code == 200
        assert s.json()["status"] == "suspended"
        # unsuspend
        s2 = requests.post(f"{API}/admin/users/{uid}/suspend", headers=auth_headers(admin_token))
        assert s2.json()["status"] == "active"
        # verify badge
        v = requests.post(f"{API}/admin/users/{uid}/verify", headers=auth_headers(admin_token))
        assert v.status_code == 200
        # delete
        d = requests.delete(f"{API}/admin/users/{uid}", headers=auth_headers(admin_token))
        assert d.status_code == 200

    def test_admin_resolve_report(self, admin_token, maya_token, jordan_token):
        # create a report first
        rp = requests.post(f"{API}/report", headers=auth_headers(maya_token["token"]),
                           json={"reported_user_id": jordan_token["id"], "reason": "test", "details": "TEST"})
        rid = rp.json()["id"]
        r = requests.post(f"{API}/admin/reports/{rid}/resolve", headers=auth_headers(admin_token))
        assert r.status_code == 200
