@router.get("/recipient/{cr_id}/history")
async def recipient_history(cr_id: int, db: AsyncSession = Depends(get_db),
                            user: User = Depends(get_current_user)):
    """Full attempt history for one campaign recipient (modal in Logs page)."""
    cr = await db.get(CampaignRecipient, cr_id)
    if not cr:
        raise HTTPException(status_code=404, detail="Recipient not found")
    camp = await db.get(Campaign, cr.campaign_id)
    if not camp or camp.user_id != user.id:
        raise HTTPException(status_code=404, detail="Recipient not found")
    attempts = (await db.execute(
        select(SendAttempt).where(SendAttempt.campaign_recipient_id == cr_id)
        .order_by(SendAttempt.id.asc()))).scalars().all()
    events = (await db.execute(
        select(EmailEvent).where(EmailEvent.campaign_recipient_id == cr_id)
        .order_by(EmailEvent.id.asc()))).scalars().all()
    return {
        "recipient": {
            "id": cr.id, "email": cr.email, "status": cr.status, "seq": cr.seq,
            "subject_id": cr.generated_subject_id, "template_id": cr.generated_template_id,
            "invoice_number": cr.generated_invoice_number,
            "amount": cr.generated_amount, "message_id": cr.message_id,
            "last_error": cr.last_error,
        },
        "attempts": [_ser_attempt(a) for a in attempts],
        "events": [{
            "id": e.id, "event_type": e.event_type, "detail": (e.detail or "")[:300],
            "message_id": e.message_id,
            "created_at": e.created_at.isoformat() if e.created_at else None,
        } for e in events],
    }
