@router.get("/list")
async def list_phones(active_only: bool = Query(False),
                      search: str = Query(""),
                      page: int = Query(1, ge=1),
                      page_size: int = Query(200, ge=1, le=500),
                      db: AsyncSession = Depends(get_db),
                      user: User = Depends(get_current_user)):
    q = select(CompanyPhoneNumber).where(CompanyPhoneNumber.user_id == user.id)
    if active_only:
        q = q.where(CompanyPhoneNumber.is_active == True)  # noqa: E712
    if search:
        q = q.where(CompanyPhoneNumber.phone.ilike(f"%{search}%")
                    | CompanyPhoneNumber.label.ilike(f"%{search}%"))
    total = (await db.execute(
        select(func.count()).select_from(q.subquery()))).scalar() or 0
    rows = (await db.execute(
        q.order_by(CompanyPhoneNumber.id.desc()).offset((page - 1) * page_size).limit(page_size)
    )).scalars().all()
    return {"phones": [_serialize(p) for p in rows],
            "total": total, "page": page, "page_size": page_size}


@router.post("/add")
async def add_phone(payload: PhoneIn, db: AsyncSession = Depends(get_db),
                    user: User = Depends(get_current_user),
                    _: None = Depends(require_csrf)):
    """Add one phone number manually."""
    dup = await db.execute(
        select(CompanyPhoneNumber).where(CompanyPhoneNumber.user_id == user.id,
                                         CompanyPhoneNumber.phone == payload.phone))
    if dup.first():
        raise HTTPException(status_code=409, detail="Phone number already exists")
    p = CompanyPhoneNumber(user_id=user.id, phone=payload.phone, label=payload.label.strip())
    db.add(p)
    await db.commit()
    await log_audit(db, user.id, "phone.add", "company_phone", p.id, {"label": p.label})
    return _serialize(p)


@router.post("/import")
===ATT-DL===
@router.get("/{att_id}")
async def get_attachment(att_id: int, user: User = Depends(get_current_user),
                         db: AsyncSession = Depends(get_db)):
    att = await _get_own(db, user, att_id)
    out = _serialize(att)
    if att.is_html_attachment:
        out["html_body"] = att.html_attachment_body
    return out


@router.get("/{att_id}/download")
async def download_attachment(att_id: int, user: User = Depends(get_current_user),
                              db: AsyncSession = Depends(get_db)):
    att = await _get_own(db, user, att_id)
    if att.is_html_attachment or not att.stored_path:
        # inline HTML attachment: return as a file download
        from fastapi.responses import Response
        content = (att.html_attachment_body or "").encode("utf-8")
        return Response(content, media_type="text/html",
                        headers={"Content-Disposition": f'attachment; filename="{att.filename}"'})
    loop = asyncio.get_event_loop()
    path = await loop.run_in_executor(None, att_svc.safe_join, att.stored_path)
    if not path:
        raise HTTPException(status_code=404, detail="Stored file missing")
    return FileResponse(path, filename=att.filename, media_type=att.mime_type or "application/octet-stream")


@router.get("/{att_id}/preview")
async def preview_attachment(att_id: int, user: User = Depends(get_current_user),
                             db: AsyncSession = Depends(get_db)):
    """Preview: images and PDFs return base64 data URI; text/html return content."""
    att = await _get_own(db, user, att_id)

    if att.is_html_attachment:
        body = att.html_attachment_body or ""
        return {"previewable": True, "kind": "html",
                "content": sanitize_preview(body)[:50000]}

    content = await asyncio.get_event_loop().run_in_executor(
        None, lambda: att_svc.read_attachment_bytes(att) or b"")
    if not content:
        return {"previewable": False, "reason": "no_content"}
    if len(content) > 2 * 1024 * 1024:
        return {"previewable": False, "reason": "too_large", "size": len(content)}
    if att.kind in ("image", "pdf"):
        mime = att.mime_type or ("application/pdf" if att.kind == "pdf" else "image/png")
        return {"previewable": True, "kind": att.kind,
                "data_uri": f"data:{mime};base64,{base64.b64encode(content).decode()}"}
    if att.kind in ("text", "html", "file"):
        try:
            text = content.decode("utf-8")
        except UnicodeDecodeError:
            return {"previewable": False, "reason": "binary"}
        if att.mime_type == "text/html":
            text = sanitize_preview(text)
        return {"previewable": True, "kind": "text", "content": text[:20000]}
    return {"previewable": False, "reason": "unsupported_kind"}


@router.post("/{att_id}/delete")
