"""Gmail API operations: list, fetch, batch-delete messages."""

import base64
import email
import time
from collections.abc import Iterator
from typing import Any

from googleapiclient.errors import HttpError

_MAX_BATCH = 1000          # Gmail batchDelete hard limit
_PAGE_SIZE = 500           # messages.list page size (max 500)
_RETRY_STATUSES = {429, 500, 502, 503, 504}
_RETRY_WAIT = 5            # seconds between retries


def _retry(fn, *args, retries: int = 5, **kwargs):
    """Call fn with simple exponential back-off on transient errors."""
    wait = _RETRY_WAIT
    for attempt in range(retries):
        try:
            return fn(*args, **kwargs)
        except HttpError as exc:
            if exc.status_code in _RETRY_STATUSES and attempt < retries - 1:
                time.sleep(wait)
                wait *= 2
            else:
                raise


def list_message_ids(service, year: int, label_ids: list[str] | None = None) -> Iterator[str]:
    """
    Yield all message IDs in the given calendar year.

    Uses Gmail's `after:`/`before:` date filters so the query is server-side.
    """
    query = f"after:{year}/01/01 before:{year + 1}/01/01"
    kwargs: dict[str, Any] = {
        "userId": "me",
        "q": query,
        "maxResults": _PAGE_SIZE,
        "includeSpamTrash": True,
    }
    if label_ids:
        kwargs["labelIds"] = label_ids

    while True:
        result = _retry(service.users().messages().list(**kwargs).execute)
        messages = result.get("messages", [])
        for msg in messages:
            yield msg["id"]

        page_token = result.get("nextPageToken")
        if not page_token:
            break
        kwargs["pageToken"] = page_token


def fetch_raw_message(service, msg_id: str) -> bytes:
    """Fetch a message in RFC 2822 format (raw base64url-encoded)."""
    result = _retry(
        service.users().messages().get(
            userId="me", id=msg_id, format="raw"
        ).execute
    )
    return base64.urlsafe_b64decode(result["raw"])


def fetch_message_metadata(service, msg_id: str) -> dict:
    """Fetch lightweight metadata (headers only, no body)."""
    return _retry(
        service.users().messages().get(
            userId="me", id=msg_id, format="metadata",
            metadataHeaders=["From", "To", "Subject", "Date"],
        ).execute
    )


def batch_delete(service, msg_ids: list[str]) -> None:
    """
    Permanently delete messages by ID.
    Gmail batchDelete is limited to 1 000 IDs per call; we chunk automatically.
    Note: this bypasses Trash — messages are gone immediately.
    """
    for i in range(0, len(msg_ids), _MAX_BATCH):
        chunk = msg_ids[i : i + _MAX_BATCH]
        _retry(
            service.users().messages().batchDelete(
                userId="me", body={"ids": chunk}
            ).execute
        )


def batch_trash(service, msg_ids: list[str]) -> None:
    """Move messages to Trash (recoverable for 30 days) instead of hard-delete."""
    # Gmail has no batchTrash endpoint; we use threads.trash where possible,
    # but messages.trash is the safest per-message call.
    for msg_id in msg_ids:
        _retry(service.users().messages().trash(userId="me", id=msg_id).execute)
