27:@router.get("")
28:async def list_templates(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
42:@router.post("")
43:async def create_template(payload: TemplateIn, user: User = Depends(get_current_user),
62:@router.post("/upload")
63:async def upload_template(file: UploadFile = File(...), name: str = Form(""),
81:@router.post("/upload-multiple")
82:async def upload_multiple(files: list[UploadFile] = File(...),
101:@router.get("/{template_id}")
102:async def get_template(template_id: int, user: User = Depends(get_current_user),
113:@router.put("/{template_id}")
114:async def edit_template(template_id: int, payload: TemplateIn,
129:@router.post("/{template_id}/duplicate")
130:async def duplicate_template(template_id: int, user: User = Depends(get_current_user),
142:@router.delete("/{template_id}")
143:async def delete_template(template_id: int, user: User = Depends(get_current_user),
153:@router.post("/clear-all")
154:async def clear_all(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
160:@router.post("/{template_id}/preview")
161:async def preview_template(template_id: int, sample: dict = {},
191:@router.post("/{template_id}/test-pdf")
192:async def test_pdf(template_id: int, sample: dict = {},
===
24:    """Assigns sender accounts per message: sequential or weighted, with limits & cooldown."""
26:    def __init__(self, accounts: list, strategy: str, weights: dict = None, max_per_account: int = 200):
28:        self.strategy = strategy if strategy in ("sequential", "weighted") else "sequential"
29:        self.weights = weights or {}
57:        if self.strategy == "weighted":
61:                    w = int(self.weights.get(str(a.id), 1))
216:    rot = RotationAssigner(accounts, campaign.rotation_strategy, campaign.rotation_weights,
===TPLLIST
async def list_templates(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
    rows = (await db.execute(select(EmailTemplate).where(EmailTemplate.user_id == user.id)
                             .order_by(EmailTemplate.id))).scalars().all()
    out = []
    for t in rows:
        analysis = analyze_template_variables(t.body_html)
        out.append({"id": t.id, "name": t.name, "subject_hint": t.subject_hint,
                    "source": t.source, "sequence_order": t.sequence_order,
                    "size_html": len(t.body_html), "variables": analysis["variables"],
                    "unknown_variables": analysis["unknown"],
                    "created_at": t.created_at.isoformat()})
    return {"templates": out, "count": len(out)}


@router.post("")
async def create_template(payload: TemplateIn, user: User = Depends(get_current_user),
                          db: AsyncSession = Depends(get_db)):
    if not payload.body_html.strip():
        raise HTTPException(400, "HTML body is required")
    if len(payload.body_html) > 1024 * 1024:
        raise HTTPException(413, "Template too large (max 1 MB)")
    problems = []
    if re.search(r"<script", payload.body_html, re.I):
        problems.append("Script tags are not allowed")
    if problems:
        raise HTTPException(400, problems)
    row = EmailTemplate(user_id=user.id, name=payload.name[:250], body_html=payload.body_html,
                        body_text=payload.body_text, subject_hint=payload.subject_hint[:500],
                        source="paste")
    db.add(row)
    await db.commit()
    return {"id": row.id, "name": row.name}


@router.post("/upload")
async def upload_template(file: UploadFile = File(...), name: str = Form(""),
