"""Campaign engine: materialization, rotation, sequencing, assignment.

The engine generates per-recipient values (invoice number, amount, shipping, subject,
template, phone, sender account) with collision-checked uniqueness and full audit data.
"""
import random
from datetime import date, datetime, timedelta, timezone
from typing import List, Optional, Sequence

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models import (Campaign, CampaignRecipient, EmailTemplate, GmailAccount,
                        SubjectLine, CompanyPhoneNumber, Recipient, SuppressionEntry)
from app.services.variable_engine import VariableEngine, GeneratedValues
from app.core.config import settings


def utcnow():
    return datetime.now(timezone.utc)


class RotationAssigner:
    """Assigns sender accounts per message: sequential or weighted, with limits & cooldown."""

    def __init__(self, accounts: list, strategy: str, weights: dict = None, max_per_account: int = 200):
        self.accounts = [a for a in accounts if a.status == "authorized" and a.is_active]
        self.strategy = strategy if strategy in ("sequential", "weighted") else "sequential"
        self.weights = weights or {}
        self.max_per_account = max_per_account
        self.usage = {a.id: 0 for a in self.accounts}
        self.cooling = {a.id: False for a in self.accounts}
        self._seq_index = 0
        self._pool = []
        self._pool_pos = 0

    def _available(self):
        avail = []
        for a in self.accounts:
            if self.usage[a.id] >= self.max_per_account:
---SEND---
"""Send task: real Gmail API/SMTP send with retry, backoff, DLQ, pause/cancel, rate limits.

Status model (no fake success):
  queued -> processing -> sent (Gmail accepted, message ID recorded)
  temporary errors -> retry with exponential backoff + jitter, dead-letter after max attempts
  permanent errors -> perm_failed (no retry)
  suppressed / skipped -> dropped pre-send
"""
import logging
import random
import re as _re
import time
from datetime import date, datetime, timedelta, timezone

import googleapiclient.errors
import redis as redis_lib
from celery import Task
from sqlalchemy import select

from app.core.config import settings
from app.db.session import SyncSession
from app.models import (Attachment, Campaign, CampaignRecipient, EmailEvent,
                        EmailTemplate, GmailAccount, OAuthToken, Recipient,
                        SendAttempt, SendJob, SubjectLine, SuppressionEntry)
from app.services import attachments_service, message_builder
from app.services.gmail_service import build_gmail_client_from_account
from app.services.variable_engine import GeneratedValues, VariableEngine
from app.worker.celery_app import celery_app

log = logging.getLogger("send")

try:
    rdb = redis_lib.from_url(settings.REDIS_URL, socket_timeout=2)
except Exception:
    rdb = None

TEMPORARY_PATTERNS = ["rate limit", "rate-limit", "429", "quota", "backend error", "transient",
                      "temporarily", "503", "502", "500", "timeout", "connection", "try again",
                      "resourceexhausted", "tempfail", "unavailable", "internal error", "deadline"]
PERMANENT_PATTERNS = ["invalid grant", "401", "403", "unauthorized", "forbidden",
                      "invalidrecipient", "failedprecondition", "invalid argument", "malformed",
                      "does not exist", "no such user", "550", "553", "554", "rejected",
                      "policy violation", "disabled", "authentication"]


def classify_error(msg: str) -> str:
    m = (msg or "").lower()
    for p in TEMPORARY_PATTERNS:
        if p in m:
            return "temporary"
    for p in PERMANENT_PATTERNS:
        if p in m:
            return "permanent"
    return "unknown"


def now():
    return datetime.now(timezone.utc)


