from dotenv import load_dotenv
from pathlib import Path
import os

ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')

from fastapi import FastAPI, APIRouter, HTTPException, Depends, Request, Response, Query
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel, Field, EmailStr
from typing import List, Optional
import uuid
import logging
import bcrypt
import jwt
import secrets
from datetime import datetime, timezone, timedelta

# ---------------- DB ----------------
mongo_url = os.environ['MONGO_URL']
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ['DB_NAME']]

app = FastAPI()
api_router = APIRouter(prefix="/api")

JWT_ALGORITHM = "HS256"
JWT_SECRET = os.environ["JWT_SECRET"]

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("qrcupid")


def now_utc():
    return datetime.now(timezone.utc)


def iso(dt):
    return dt.isoformat()


# ---------------- Security helpers ----------------
def hash_password(password: str) -> str:
    return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")


def verify_password(plain: str, hashed: str) -> bool:
    try:
        return bcrypt.checkpw(plain.encode("utf-8"), hashed.encode("utf-8"))
    except Exception:
        return False


def create_access_token(user_id: str, email: str) -> str:
    payload = {"sub": user_id, "email": email,
               "exp": now_utc() + timedelta(days=7), "type": "access"}
    return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)


def public_user(u: dict) -> dict:
    if not u:
        return u
    u = dict(u)
    u.pop("_id", None)
    u.pop("password_hash", None)
    return u


async def get_current_user(request: Request) -> dict:
    token = request.cookies.get("access_token")
    if not token:
        auth = request.headers.get("Authorization", "")
        if auth.startswith("Bearer "):
            token = auth[7:]
    if not token:
        raise HTTPException(status_code=401, detail="Not authenticated")
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
        user = await db.users.find_one({"id": payload["sub"]})
        if not user:
            raise HTTPException(status_code=401, detail="User not found")
        if user.get("status") == "suspended":
            raise HTTPException(status_code=403, detail="Account suspended")
        return public_user(user)
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")


async def get_admin(user: dict = Depends(get_current_user)) -> dict:
    if user.get("role") != "admin":
        raise HTTPException(status_code=403, detail="Admin access required")
    return user


# ---------------- Models ----------------
class RegisterIn(BaseModel):
    email: EmailStr
    password: str
    name: str


class LoginIn(BaseModel):
    email: EmailStr
    password: str


class ForgotIn(BaseModel):
    email: EmailStr


class ResetIn(BaseModel):
    token: str
    password: str


class VerifyIn(BaseModel):
    token: str


class ProfileIn(BaseModel):
    name: Optional[str] = None
    bio: Optional[str] = None
    age: Optional[int] = None
    gender: Optional[str] = None
    orientation: Optional[str] = None
    location: Optional[str] = None
    interests: Optional[List[str]] = None
    occupation: Optional[str] = None
    education: Optional[str] = None
    photos: Optional[List[str]] = None
    avatar: Optional[str] = None
    looking_for: Optional[str] = None
    privacy_hidden: Optional[bool] = None


class MessageIn(BaseModel):
    to_user_id: str
    text: Optional[str] = None
    image: Optional[str] = None


class ReportIn(BaseModel):
    reported_user_id: str
    reason: str
    details: Optional[str] = None


class SubscribeIn(BaseModel):
    plan: str  # premium_monthly | premium_yearly
    card_number: str
    exp: str
    cvv: str
    name_on_card: str


# ---------------- Defaults ----------------
def new_user_doc(email, password, name, role="user"):
    uid = str(uuid.uuid4())
    return {
        "id": uid,
        "email": email.lower(),
        "password_hash": hash_password(password),
        "name": name,
        "role": role,
        "status": "active",
        "email_verified": False,
        "membership": "free",
        "membership_expires": None,
        "verified_badge": False,
        "bio": "",
        "age": None,
        "gender": "",
        "orientation": "",
        "location": "",
        "interests": [],
        "occupation": "",
        "education": "",
        "photos": [],
        "avatar": "",
        "looking_for": "",
        "privacy_hidden": False,
        "slug": uid[:8],
        "last_active": iso(now_utc()),
        "created_at": iso(now_utc()),
    }


# ---------------- Auth endpoints ----------------
@api_router.post("/auth/register")
async def register(body: RegisterIn, response: Response):
    email = body.email.lower()
    if await db.users.find_one({"email": email}):
        raise HTTPException(status_code=400, detail="Email already registered")
    doc = new_user_doc(email, body.password, body.name)
    # email verification token (mocked - logged)
    vtoken = secrets.token_urlsafe(24)
    doc["verify_token"] = vtoken
    await db.users.insert_one(doc)
    logger.info(f"[EMAIL] Verify {email}: token={vtoken}")
    token = create_access_token(doc["id"], email)
    response.set_cookie("access_token", token, httponly=True, secure=False,
                        samesite="lax", max_age=604800, path="/")
    out = public_user(doc)
    out["token"] = token
    out["verify_token"] = vtoken  # surfaced for in-app mock verification
    return out


@api_router.post("/auth/login")
async def login(body: LoginIn, response: Response):
    email = body.email.lower()
    ip = "local"
    ident = f"{ip}:{email}"
    attempt = await db.login_attempts.find_one({"identifier": ident})
    if attempt and attempt.get("count", 0) >= 5:
        locked_until = attempt.get("locked_until")
        if locked_until and datetime.fromisoformat(locked_until) > now_utc():
            raise HTTPException(status_code=429, detail="Too many attempts. Try again later.")
    user = await db.users.find_one({"email": email})
    if not user or not verify_password(body.password, user["password_hash"]):
        await db.login_attempts.update_one(
            {"identifier": ident},
            {"$inc": {"count": 1},
             "$set": {"locked_until": iso(now_utc() + timedelta(minutes=15))}},
            upsert=True)
        raise HTTPException(status_code=401, detail="Invalid email or password")
    if user.get("status") == "suspended":
        raise HTTPException(status_code=403, detail="Account suspended. Contact support.")
    await db.login_attempts.delete_one({"identifier": ident})
    await db.users.update_one({"id": user["id"]}, {"$set": {"last_active": iso(now_utc())}})
    token = create_access_token(user["id"], email)
    response.set_cookie("access_token", token, httponly=True, secure=False,
                        samesite="lax", max_age=604800, path="/")
    out = public_user(user)
    out["token"] = token
    return out


@api_router.post("/auth/logout")
async def logout(response: Response):
    response.delete_cookie("access_token", path="/")
    return {"ok": True}


@api_router.get("/auth/me")
async def me(user: dict = Depends(get_current_user)):
    return user


@api_router.post("/auth/verify-email")
async def verify_email(body: VerifyIn, user: dict = Depends(get_current_user)):
    full = await db.users.find_one({"id": user["id"]})
    if full.get("verify_token") != body.token:
        raise HTTPException(status_code=400, detail="Invalid verification token")
    await db.users.update_one({"id": user["id"]},
                              {"$set": {"email_verified": True, "verified_badge": True},
                               "$unset": {"verify_token": ""}})
    return {"ok": True, "email_verified": True}


@api_router.post("/auth/forgot-password")
async def forgot_password(body: ForgotIn):
    user = await db.users.find_one({"email": body.email.lower()})
    token = secrets.token_urlsafe(32)
    if user:
        await db.password_reset_tokens.insert_one({
            "token": token, "user_id": user["id"],
            "expires_at": iso(now_utc() + timedelta(hours=1)), "used": False})
        logger.info(f"[EMAIL] Reset link for {body.email}: token={token}")
    # Always return token in mock mode so user can proceed in-app
    return {"ok": True, "reset_token": token if user else None}


@api_router.post("/auth/reset-password")
async def reset_password(body: ResetIn):
    rec = await db.password_reset_tokens.find_one({"token": body.token})
    if not rec or rec.get("used"):
        raise HTTPException(status_code=400, detail="Invalid or used token")
    if datetime.fromisoformat(rec["expires_at"]) < now_utc():
        raise HTTPException(status_code=400, detail="Token expired")
    await db.users.update_one({"id": rec["user_id"]},
                              {"$set": {"password_hash": hash_password(body.password)}})
    await db.password_reset_tokens.update_one({"token": body.token}, {"$set": {"used": True}})
    return {"ok": True}


# ---------------- Profile ----------------
@api_router.put("/profile")
async def update_profile(body: ProfileIn, user: dict = Depends(get_current_user)):
    updates = {k: v for k, v in body.model_dump().items() if v is not None}
    if updates:
        updates["last_active"] = iso(now_utc())
        await db.users.update_one({"id": user["id"]}, {"$set": updates})
    fresh = await db.users.find_one({"id": user["id"]})
    return public_user(fresh)


@api_router.delete("/profile")
async def delete_account(user: dict = Depends(get_current_user)):
    await db.users.update_one({"id": user["id"]}, {"$set": {"status": "deleted"}})
    await db.users.delete_one({"id": user["id"]})
    return {"ok": True}


@api_router.get("/profile/public/{slug}")
async def public_profile(slug: str):
    u = await db.users.find_one({"slug": slug})
    if not u or u.get("status") != "active":
        raise HTTPException(status_code=404, detail="Profile not found")
    if u.get("privacy_hidden"):
        # limited public view
        return {"id": u["id"], "name": u["name"], "slug": u["slug"],
                "avatar": u.get("avatar", ""), "private": True,
                "verified_badge": u.get("verified_badge", False)}
    p = public_user(u)
    p.pop("email", None)
    p.pop("membership_expires", None)
    return p


# ---------------- Discovery ----------------
@api_router.get("/members")
async def browse_members(
    user: dict = Depends(get_current_user),
    q: Optional[str] = None,
    gender: Optional[str] = None,
    min_age: Optional[int] = None,
    max_age: Optional[int] = None,
    interest: Optional[str] = None,
    location: Optional[str] = None,
    online_now: Optional[bool] = False,
    recently_active: Optional[bool] = False,
    sort: Optional[str] = "active",
):
    blocked = set((await db.users.find_one({"id": user["id"]})).get("blocked", []))
    query = {"status": "active", "privacy_hidden": {"$ne": True},
             "id": {"$ne": user["id"]}, "role": {"$ne": "admin"}}
    if gender:
        query["gender"] = gender
    if location:
        query["location"] = {"$regex": location, "$options": "i"}
    if interest:
        query["interests"] = {"$in": [interest]}
    if min_age is not None or max_age is not None:
        age_q = {}
        if min_age is not None:
            age_q["$gte"] = min_age
        if max_age is not None:
            age_q["$lte"] = max_age
        query["age"] = age_q
    if q:
        query["$or"] = [
            {"name": {"$regex": q, "$options": "i"}},
            {"bio": {"$regex": q, "$options": "i"}},
            {"occupation": {"$regex": q, "$options": "i"}},
            {"interests": {"$regex": q, "$options": "i"}},
        ]
    cursor = db.users.find(query).limit(200)
    members = await cursor.to_list(200)
    threshold = now_utc() - timedelta(minutes=10)
    recent_threshold = now_utc() - timedelta(days=3)
    out = []
    for m in members:
        if m["id"] in blocked:
            continue
        la = m.get("last_active")
        is_online = la and datetime.fromisoformat(la) > threshold
        if online_now and not is_online:
            continue
        if recently_active and not (la and datetime.fromisoformat(la) > recent_threshold):
            continue
        pm = public_user(m)
        pm.pop("email", None)
        pm["online"] = bool(is_online)
        out.append(pm)
    if sort == "active":
        out.sort(key=lambda x: x.get("last_active", ""), reverse=True)
    return out


@api_router.get("/members/{member_id}")
async def get_member(member_id: str, user: dict = Depends(get_current_user)):
    m = await db.users.find_one({"id": member_id})
    if not m or m.get("status") != "active":
        raise HTTPException(status_code=404, detail="Member not found")
    pm = public_user(m)
    pm.pop("email", None)
    me_full = await db.users.find_one({"id": user["id"]})
    pm["liked"] = member_id in me_full.get("likes", [])
    pm["favorited"] = member_id in me_full.get("favorites", [])
    return pm


# ---------------- Likes / Favorites / Matches ----------------
@api_router.post("/like/{member_id}")
async def like_member(member_id: str, user: dict = Depends(get_current_user)):
    if member_id == user["id"]:
        raise HTTPException(status_code=400, detail="Cannot like yourself")
    target = await db.users.find_one({"id": member_id})
    if not target:
        raise HTTPException(status_code=404, detail="Member not found")
    me_full = await db.users.find_one({"id": user["id"]})
    likes = set(me_full.get("likes", []))
    matched = False
    if member_id in likes:
        likes.discard(member_id)
    else:
        likes.add(member_id)
        if user["id"] in target.get("likes", []):
            matched = True
    await db.users.update_one({"id": user["id"]}, {"$set": {"likes": list(likes)}})
    return {"liked": member_id in likes, "matched": matched}


@api_router.post("/favorite/{member_id}")
async def favorite_member(member_id: str, user: dict = Depends(get_current_user)):
    me_full = await db.users.find_one({"id": user["id"]})
    favs = set(me_full.get("favorites", []))
    if member_id in favs:
        favs.discard(member_id)
    else:
        favs.add(member_id)
    await db.users.update_one({"id": user["id"]}, {"$set": {"favorites": list(favs)}})
    return {"favorited": member_id in favs}


@api_router.get("/favorites")
async def list_favorites(user: dict = Depends(get_current_user)):
    me_full = await db.users.find_one({"id": user["id"]})
    ids = me_full.get("favorites", [])
    members = await db.users.find({"id": {"$in": ids}, "status": "active"}).to_list(200)
    return [{k: v for k, v in public_user(m).items() if k != "email"} for m in members]


@api_router.get("/matches")
async def list_matches(user: dict = Depends(get_current_user)):
    me_full = await db.users.find_one({"id": user["id"]})
    my_likes = set(me_full.get("likes", []))
    if not my_likes:
        return []
    candidates = await db.users.find({"id": {"$in": list(my_likes)}, "status": "active"}).to_list(200)
    matches = [c for c in candidates if user["id"] in c.get("likes", [])]
    return [{k: v for k, v in public_user(m).items() if k != "email"} for m in matches]


@api_router.get("/likes/received")
async def likes_received(user: dict = Depends(get_current_user)):
    # premium feature: who liked you
    me_full = await db.users.find_one({"id": user["id"]})
    if me_full.get("membership") == "free":
        raise HTTPException(status_code=402, detail="Premium feature. Upgrade to see who likes you.")
    likers = await db.users.find({"likes": user["id"], "status": "active"}).to_list(200)
    return [{k: v for k, v in public_user(m).items() if k != "email"} for m in likers]


# ---------------- Messaging ----------------
def thread_id(a, b):
    return "__".join(sorted([a, b]))


@api_router.post("/messages")
async def send_message(body: MessageIn, user: dict = Depends(get_current_user)):
    target = await db.users.find_one({"id": body.to_user_id})
    if not target:
        raise HTTPException(status_code=404, detail="Recipient not found")
    if user["id"] in target.get("blocked", []):
        raise HTTPException(status_code=403, detail="You cannot message this user")
    msg = {
        "id": str(uuid.uuid4()),
        "thread": thread_id(user["id"], body.to_user_id),
        "from_user_id": user["id"],
        "to_user_id": body.to_user_id,
        "text": body.text or "",
        "image": body.image or "",
        "read": False,
        "created_at": iso(now_utc()),
    }
    await db.messages.insert_one(msg)
    msg.pop("_id", None)
    return msg


@api_router.get("/messages/threads")
async def get_threads(user: dict = Depends(get_current_user)):
    msgs = await db.messages.find({"$or": [{"from_user_id": user["id"]},
                                           {"to_user_id": user["id"]}]}).sort("created_at", -1).to_list(2000)
    threads = {}
    for m in msgs:
        other = m["to_user_id"] if m["from_user_id"] == user["id"] else m["from_user_id"]
        if other not in threads:
            threads[other] = {"other_id": other, "last_text": m.get("text") or "[image]",
                              "last_at": m["created_at"], "unread": 0}
        if m["to_user_id"] == user["id"] and not m["read"]:
            threads[other]["unread"] += 1
    # attach user info
    ids = list(threads.keys())
    users = await db.users.find({"id": {"$in": ids}}).to_list(500)
    umap = {u["id"]: u for u in users}
    out = []
    for oid, t in threads.items():
        u = umap.get(oid)
        if not u:
            continue
        t["name"] = u["name"]
        t["avatar"] = u.get("avatar", "")
        out.append(t)
    out.sort(key=lambda x: x["last_at"], reverse=True)
    return out


@api_router.get("/messages/{other_id}")
async def get_conversation(other_id: str, user: dict = Depends(get_current_user)):
    tid = thread_id(user["id"], other_id)
    msgs = await db.messages.find({"thread": tid}).sort("created_at", 1).to_list(2000)
    # mark received as read (read receipts)
    await db.messages.update_many({"thread": tid, "to_user_id": user["id"], "read": False},
                                  {"$set": {"read": True}})
    for m in msgs:
        m.pop("_id", None)
    other = await db.users.find_one({"id": other_id})
    return {"messages": msgs,
            "other": {"id": other_id, "name": other["name"] if other else "User",
                      "avatar": other.get("avatar", "") if other else ""}}


# ---------------- Block / Report ----------------
@api_router.post("/block/{member_id}")
async def block_member(member_id: str, user: dict = Depends(get_current_user)):
    me_full = await db.users.find_one({"id": user["id"]})
    blocked = set(me_full.get("blocked", []))
    if member_id in blocked:
        blocked.discard(member_id)
    else:
        blocked.add(member_id)
    await db.users.update_one({"id": user["id"]}, {"$set": {"blocked": list(blocked)}})
    return {"blocked": member_id in blocked}


@api_router.post("/report")
async def report_member(body: ReportIn, user: dict = Depends(get_current_user)):
    rec = {
        "id": str(uuid.uuid4()),
        "reporter_id": user["id"],
        "reported_user_id": body.reported_user_id,
        "reason": body.reason,
        "details": body.details or "",
        "status": "open",
        "created_at": iso(now_utc()),
    }
    await db.reports.insert_one(rec)
    rec.pop("_id", None)
    return rec


# ---------------- Payments / Membership (Authorize.Net mock) ----------------
PLANS = {
    "premium": {"name": "Premium", "price": 5.00, "days": 30},
}

CARD_PRODUCT = {"name": "Printed QR Cards (100)", "price": 15.00, "quantity": 100}


@api_router.get("/plans")
async def get_plans():
    return [{"id": k, **v} for k, v in PLANS.items()]


@api_router.post("/subscribe")
async def subscribe(body: SubscribeIn, user: dict = Depends(get_current_user)):
    plan = PLANS.get(body.plan)
    if not plan:
        raise HTTPException(status_code=400, detail="Invalid plan")
    # MOCK Authorize.Net transaction
    card = body.card_number.replace(" ", "")
    if len(card) < 13 or not card.isdigit():
        raise HTTPException(status_code=402, detail="Payment declined: invalid card")
    txn_id = "ANET-" + secrets.token_hex(8).upper()
    expires = now_utc() + timedelta(days=plan["days"])
    await db.users.update_one({"id": user["id"]},
                              {"$set": {"membership": "premium",
                                        "membership_expires": iso(expires)}})
    payment = {
        "id": str(uuid.uuid4()),
        "user_id": user["id"],
        "plan": body.plan,
        "plan_name": plan["name"],
        "amount": plan["price"],
        "transaction_id": txn_id,
        "card_last4": card[-4:],
        "status": "success",
        "created_at": iso(now_utc()),
    }
    await db.payments.insert_one(payment)
    payment.pop("_id", None)
    fresh = await db.users.find_one({"id": user["id"]})
    return {"payment": payment, "user": public_user(fresh)}


@api_router.post("/subscription/cancel")
async def cancel_subscription(user: dict = Depends(get_current_user)):
    await db.users.update_one({"id": user["id"]},
                              {"$set": {"membership": "free", "membership_expires": None}})
    fresh = await db.users.find_one({"id": user["id"]})
    return public_user(fresh)


class OrderCardsIn(BaseModel):
    card_number: str
    exp: str
    cvv: str
    name_on_card: str
    shipping_address: Optional[str] = None


@api_router.post("/order-cards")
async def order_cards(body: OrderCardsIn, user: dict = Depends(get_current_user)):
    card = body.card_number.replace(" ", "")
    if len(card) < 13 or not card.isdigit():
        raise HTTPException(status_code=402, detail="Payment declined: invalid card")
    txn_id = "ANET-" + secrets.token_hex(8).upper()
    payment = {
        "id": str(uuid.uuid4()),
        "user_id": user["id"],
        "plan": "cards",
        "plan_name": CARD_PRODUCT["name"],
        "amount": CARD_PRODUCT["price"],
        "quantity": CARD_PRODUCT["quantity"],
        "transaction_id": txn_id,
        "card_last4": card[-4:],
        "shipping_address": body.shipping_address or "",
        "status": "success",
        "created_at": iso(now_utc()),
    }
    await db.payments.insert_one(payment)
    payment.pop("_id", None)
    return {"payment": payment}


@api_router.get("/payments/history")
async def payment_history(user: dict = Depends(get_current_user)):
    pays = await db.payments.find({"user_id": user["id"]}).sort("created_at", -1).to_list(200)
    for p in pays:
        p.pop("_id", None)
    return pays


# ---------------- Admin ----------------
@api_router.get("/admin/stats")
async def admin_stats(admin: dict = Depends(get_admin)):
    total_users = await db.users.count_documents({"role": {"$ne": "admin"}})
    premium = await db.users.count_documents({"membership": "premium"})
    suspended = await db.users.count_documents({"status": "suspended"})
    open_reports = await db.reports.count_documents({"status": "open"})
    payments = await db.payments.find({"status": "success"}).to_list(5000)
    revenue = sum(p["amount"] for p in payments)
    total_messages = await db.messages.count_documents({})
    # revenue by month
    by_month = {}
    for p in payments:
        m = p["created_at"][:7]
        by_month[m] = by_month.get(m, 0) + p["amount"]
    revenue_series = [{"month": k, "revenue": round(v, 2)} for k, v in sorted(by_month.items())]
    # membership distribution
    free_users = total_users - premium
    return {
        "total_users": total_users,
        "premium_users": premium,
        "free_users": free_users,
        "suspended_users": suspended,
        "open_reports": open_reports,
        "total_revenue": round(revenue, 2),
        "total_payments": len(payments),
        "total_messages": total_messages,
        "revenue_series": revenue_series,
        "membership_distribution": [
            {"name": "Free", "value": free_users},
            {"name": "Premium", "value": premium},
        ],
    }


@api_router.get("/admin/users")
async def admin_users(admin: dict = Depends(get_admin), q: Optional[str] = None):
    query = {"role": {"$ne": "admin"}}
    if q:
        query["$or"] = [{"name": {"$regex": q, "$options": "i"}},
                        {"email": {"$regex": q, "$options": "i"}}]
    users = await db.users.find(query).sort("created_at", -1).to_list(500)
    return [public_user(u) for u in users]


@api_router.post("/admin/users/{user_id}/suspend")
async def admin_suspend(user_id: str, admin: dict = Depends(get_admin)):
    u = await db.users.find_one({"id": user_id})
    if not u:
        raise HTTPException(status_code=404, detail="User not found")
    new_status = "active" if u.get("status") == "suspended" else "suspended"
    await db.users.update_one({"id": user_id}, {"$set": {"status": new_status}})
    return {"status": new_status}


@api_router.delete("/admin/users/{user_id}")
async def admin_delete(user_id: str, admin: dict = Depends(get_admin)):
    await db.users.delete_one({"id": user_id})
    return {"ok": True}


@api_router.post("/admin/users/{user_id}/verify")
async def admin_verify(user_id: str, admin: dict = Depends(get_admin)):
    u = await db.users.find_one({"id": user_id})
    new_val = not u.get("verified_badge", False)
    await db.users.update_one({"id": user_id}, {"$set": {"verified_badge": new_val}})
    return {"verified_badge": new_val}


@api_router.get("/admin/reports")
async def admin_reports(admin: dict = Depends(get_admin)):
    reports = await db.reports.find({}).sort("created_at", -1).to_list(500)
    out = []
    for r in reports:
        r.pop("_id", None)
        reporter = await db.users.find_one({"id": r["reporter_id"]})
        reported = await db.users.find_one({"id": r["reported_user_id"]})
        r["reporter_name"] = reporter["name"] if reporter else "Unknown"
        r["reported_name"] = reported["name"] if reported else "Unknown"
        out.append(r)
    return out


@api_router.post("/admin/reports/{report_id}/resolve")
async def resolve_report(report_id: str, admin: dict = Depends(get_admin)):
    await db.reports.update_one({"id": report_id}, {"$set": {"status": "resolved"}})
    return {"ok": True}


@api_router.get("/admin/payments")
async def admin_payments(admin: dict = Depends(get_admin)):
    pays = await db.payments.find({}).sort("created_at", -1).to_list(1000)
    out = []
    for p in pays:
        p.pop("_id", None)
        u = await db.users.find_one({"id": p["user_id"]})
        p["user_name"] = u["name"] if u else "Unknown"
        p["user_email"] = u["email"] if u else ""
        out.append(p)
    return out


@api_router.get("/admin/moderation")
async def admin_moderation(admin: dict = Depends(get_admin)):
    # users with photos for moderation
    users = await db.users.find({"role": {"$ne": "admin"},
                                 "$or": [{"photos": {"$ne": []}}, {"avatar": {"$ne": ""}}]}).to_list(500)
    return [public_user(u) for u in users]


# ---------------- Health ----------------
@api_router.get("/")
async def root():
    return {"message": "QRCupid API"}


app.include_router(api_router)

app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_origins=os.environ.get('CORS_ORIGINS', '*').split(','),
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.on_event("startup")
async def startup():
    await db.users.create_index("email", unique=True)
    await db.users.create_index("slug")
    await db.users.create_index("id", unique=True)
    await db.messages.create_index("thread")
    await db.login_attempts.create_index("identifier")
    # seed admin
    admin_email = os.environ["ADMIN_EMAIL"].lower()
    admin_password = os.environ["ADMIN_PASSWORD"]
    existing = await db.users.find_one({"email": admin_email})
    if not existing:
        doc = new_user_doc(admin_email, admin_password, "Admin", role="admin")
        doc["email_verified"] = True
        doc["verified_badge"] = True
        await db.users.insert_one(doc)
        logger.info("Seeded admin user")
    elif not verify_password(admin_password, existing["password_hash"]):
        await db.users.update_one({"email": admin_email},
                                  {"$set": {"password_hash": hash_password(admin_password)}})


@app.on_event("shutdown")
async def shutdown():
    client.close()
