26:def build_mime_message(
83:def validate_html(html: str) -> List[str]:
104:def html_to_pdf(html: str) -> bytes:
129:def generate_attachment_filename(kind: str, invoice_number: str) -> str:
---
    def send_mime_api(self, mime_message) -> dict:
        """Send via Gmail API users.messages.send. Returns real provider response."""
        service = self._api()
        raw = base64.urlsafe_b64encode(mime_message.as_bytes()).decode()
        body = {"raw": raw}
        sent = service.users().messages().send(userId="me", body=body).execute()
        return {"ok": True, "message_id": sent.get("id", ""), "thread_id": sent.get("threadId", ""),
                "label_ids": sent.get("labelIds", []), "raw_response": sent}

    # -- SMTP send (XOAUTH2, no password) -------------------------------------
    def send_mime_smtp(self, mime_message) -> dict:
        import smtplib
        from email.utils import parseaddr
        self._ensure_valid()
        access_token = self._creds.token
        auth_string = f"user={self.account_email}\x01auth=Bearer {access_token}\x01\x01"
        with smtplib.SMTP("smtp.gmail.com", 587, timeout=30) as smtp:
            smtp.ehlo()
            smtp.starttls(context=__import__("ssl").create_default_context())
            smtp.ehlo()
            code, response = smtp.docmd("AUTH", "XOAUTH2 " + base64.b64encode(auth_string.encode()).decode())
            refused = smtp.sendmail(self.account_email, [parseaddr(mime_message["To"])[1]], mime_message.as_bytes())
        if refused:
            return {"ok": False, "error": f"SMTP refused recipients: {refused}", "error_class": "permanent"}
        return {"ok": True, "message_id": "", "thread_id": "", "raw_response": f"250 SMTP accepted ({code} {response.decode(errors='replace')})"}

    # -- Health / profile ------------------------------------------------------
    def get_profile(self) -> dict:
---CELERY---
"""Celery application: Redis broker, task routing, beat schedule."""
import logging
import os

from celery import Celery
from kombu import Queue

from app.core.config import settings

log = logging.getLogger("celery")

redis_url = settings.REDIS_URL
broker_url = redis_url if redis_url.endswith("/0") else redis_url
if not broker_url.endswith(str(settings.CELERY_BROKER_DB)):
    broker_url = broker_url.rstrip("0123456789") + str(settings.CELERY_BROKER_DB)
result_backend = broker_url.rstrip("0123456789") + str(settings.CELERY_RESULT_DB)

celery_app = Celery(
    "bulkgmailmailer",
    broker=broker_url,
    backend=result_backend,
    include=["app.tasks.send_tasks"],
)

celery_app.conf.update(
    task_serializer="json",
    accept_content=["json"],
    result_serializer="json",
    timezone="UTC",
    enable_utc=True,
    task_acks_late=True,
    task_reject_on_worker_lost=True,
    worker_prefetch_multiplier=1,   # strict sequential-like processing per worker
    task_default_queue="send",
    task_queues=(Queue("send", routing_key="send.#"), Queue("control", routing_key="control.#")),
    task_routes={"app.tasks.send_tasks.send_one": {"queue": "send"},
                 "app.tasks.send_tasks.*": {"queue": "send"}},
    broker_connection_retry_on_startup=True,
    task_track_started=True,
    task_time_limit=120,
    task_soft_time_limit=100,
    # Rate limiting is applied per-account inside tasks via Redis; global default:
    worker_concurrency=2,
)
