1:    return {"events": out, "total": total, "page": page, "page_size": page_size}
4:@router.get("/audit")
5:async def list_audit(action: str = Query(""),
26:            "detail": r.detail if isinstance(r.detail, dict) else {},
30:    return {"audit": out, "total": total, "page": page, "page_size": page_size}
33:@router.get("/dlq")
34:async def list_dlq(page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200),
43:        return {"dlq": [], "total": 0, "error": f"Redis unavailable: {e}"}
53:    return {"dlq": out[start:start + page_size], "total": total,
57:@router.get("/export")
58:async def export_logs(campaign_id: int = Query(0),
72:    def generate():
88:    return StreamingResponse(generate(), media_type="text/csv",
93:@router.get("/recipient/{cr_id}/history")
94:async def recipient_history(cr_id: int, db: AsyncSession = Depends(get_db),
99:        raise HTTPException(status_code=404, detail="Recipient not found")
102:        raise HTTPException(status_code=404, detail="Recipient not found")
111:    return {
121:            "id": e.id, "event_type": e.event_type, "detail": (e.detail or "")[:300],
===SUPP-IMPORT-TAIL===
    lines = text.splitlines()
    looks_csv = any("," in ln for ln in lines[:50])
    if looks_csv:
        for row in csv.reader(io.StringIO(text)):
            if not row or not (row[0] or "").strip():
                continue
            email = row[0].strip().lower()
            r = (row[1].strip() if len(row) > 1 else reason) or reason
            if email.lower().startswith(("email", "address")):
                continue
            if is_valid_email(email):
                entries.append((email, _norm_reason(r)))
            else:
                invalid.append(email)
    else:
        for line in lines:
            email = line.strip().lower()
            if not email or email.lower().startswith(("email", "address", "#")):
                continue
            if is_valid_email(email):
                entries.append((email, reason))
            else:
                invalid.append(email)

    if not entries:
        raise HTTPException(status_code=400,
                            detail=f"No valid emails found. Invalid: {invalid[:10]}")

    existing = set((await db.execute(
        select(SuppressionEntry.email).where(SuppressionEntry.user_id == user.id))).scalars())
    added = 0
    for email, r in entries:
        if email in existing:
            continue
        db.add(SuppressionEntry(user_id=user.id, email=email, reason=r,
                                source="import"))
        existing.add(email)
        added += 1
    await db.commit()
    await log_audit(db, user.id, "suppression.import", "suppression", None,
                    {"added": added, "invalid": len(invalid)})
    return {"added": added, "duplicates": len(entries) - added,
            "invalid": invalid[:20], "total_valid": len(entries)}


@router.post("/bulk")
===RESET===
@router.post("/reset")
async def reset_setting(payload: SettingIn, db: AsyncSession = Depends(get_db),
                        user: User = Depends(get_current_user),
                        _: None = Depends(require_csrf)):
    """Reset one setting to its default value (deletes the DB row)."""
    if payload.key not in SETTING_DEFS:
        raise HTTPException(status_code=400, detail="Unknown setting key")
    row = await db.execute(
        select(ApplicationSettings).where(ApplicationSettings.key == payload.key))
    row = row.scalar()
    if row:
        await db.delete(row)
        await db.commit()
    await log_audit(db, user.id, "settings.reset", "application_setting", None,
                    {"key": payload.key})
    return {"key": payload.key, "value": SETTING_DEFS[payload.key]["default"]}


@router.post("/reset-all")
async def reset_all_settings(db: AsyncSession = Depends(get_db),
                             user: User = Depends(get_current_user),
                             _: None = Depends(require_csrf)):
    rows = (await db.execute(
        select(ApplicationSettings).where(ApplicationSettings.key.in_(SETTING_DEFS.keys()))
    )).scalars().all()
    for r in rows:
        await db.delete(r)
    await db.commit()
    await log_audit(db, user.id, "settings.reset_all", "application_setting", None,
                    {"reset": len(rows)})
===BATCH===
@router.post("/save-batch")
async def save_batch(payload: BatchSettingsIn, db: AsyncSession = Depends(get_db),
                     user: User = Depends(get_current_user),
                     _: None = Depends(require_csrf)):
    """Save multiple settings at once (Settings page form)."""
    applied, errors = {}, {}
    for key, value in payload.values.items():
        if key not in SETTING_DEFS:
            errors[key] = "Unknown setting key"
            continue
        ok, v = _validate_value(key, value)
        if not ok:
            errors[key] = v
            continue
        row = await db.execute(
            select(ApplicationSettings).where(ApplicationSettings.key == key))
        row = row.scalar()
        if row:
            row.value = v
        else:
            db.add(ApplicationSettings(key=key, value=v,
                                       description=SETTING_DEFS[key]["label"],
                                       category=SETTING_DEFS[key]["category"]))
        applied[key] = v
    await db.commit()
    if applied:
        await log_audit(db, user.id, "settings.save_batch", "application_setting", None,
                        {"keys": list(applied.keys())})
    return {"applied": applied, "errors": errors}


@router.post("/reset")
