app/services/recipient_service.py:37:    def summary(self) -> dict:
app/services/recipient_service.py-38-        return {
app/services/recipient_service.py-39-            "total_imported": self.rows_seen,
app/services/recipient_service.py-40-            "valid_recipients": len(self.valid),
app/services/recipient_service.py-41-            "invalid_recipients": len(self.invalid),
app/services/recipient_service.py-42-            "duplicate_recipients": len(self.duplicates),
app/services/recipient_service.py-43-            "suppressed_recipients": len(self.suppressed),
app/services/recipient_service.py-44-            "bounced_recipients": len(self.bounced),
app/services/recipient_service.py-45-            "errors": self.errors,
app/services/recipient_service.py-46-        }
app/services/recipient_service.py-47-
app/services/recipient_service.py-48-
app/services/recipient_service.py-49-def is_valid_email(addr: str) -> bool:
@router.post("/import-preview")
async def import_preview(file: UploadFile = File(...),
                         user: User = Depends(get_current_user_csrf),
                         db: AsyncSession = Depends(get_db)):
    """Parse + validate an upload WITHOUT saving. Shows the full validation report."""
    content = await file.read()
    if len(content) > settings.MAX_UPLOAD_MB * 1024 * 1024:
        raise HTTPException(413, f"File too large (max {settings.MAX_UPLOAD_MB} MB)")
    fname = (file.filename or "").lower()
    if fname.endswith(".json"):
        raise HTTPException(400, "JSON recipient files are not accepted. Use CSV or TXT.")
    if not (fname.endswith(".csv") or fname.endswith(".txt")):
        raise HTTPException(400, "Only .csv and .txt files are accepted for recipients")

    if fname.endswith(".txt"):
        lines = recipient_service.parse_txt(content.decode("utf-8", errors="replace"))
        records = []
        for ln in lines:
            parts = [p.strip() for p in ln.split(",")]
            rec = {"email": parts[0]}
            if len(parts) > 1:
                rec["first_name"] = parts[1]
            if len(parts) > 2:
                rec["last_name"] = parts[2]
            records.append(rec)
        headers = ["email", "first_name", "last_name"]
        parse_errors = []
    else:
        records, headers, parse_errors = recipient_service.parse_csv(
            content.decode("utf-8", errors="replace"))

    sup, bounced = await _suppression_and_bounces(db)
    existing = set((await db.execute(select(Recipient.email))).scalars().all())
    rpt = recipient_service.validate_rows(records, sup, bounced, existing,
                                          settings.MAX_RECIPIENTS_PER_CAMPAIGN)
    return {"filename": file.filename, "headers": headers,
            "summary": rpt.summary(),
            "valid_sample": rpt.valid[:25],
            "invalid_sample": rpt.invalid[:25],
            "duplicates_sample": rpt.duplicates[:10],
            "suppressed_sample": rpt.suppressed[:10],
            "parse_errors": parse_errors[:50],
            "parsed_records": len(records)}


