"""
Async worker that processes Gmail Pub/Sub history deltas.

Usage:
  python -m fmatch.saas.workers.gmail_history_worker
"""

from __future__ import annotations

import asyncio
import json
import logging
import os
import signal
from dataclasses import dataclass
from typing import Optional

import redis.asyncio as redis

from fmatch.saas import settings
from fmatch.saas.services.gmail_support import process_gmail_history_delta

logger = logging.getLogger(__name__)

GMAIL_HISTORY_QUEUE = "gmail:history:queue"


@dataclass
class HistoryItem:
    mailbox_id: str
    history_id: Optional[str] = None

    @staticmethod
    def from_json(payload: str) -> "HistoryItem":
        data = json.loads(payload)
        mailbox_id = data.get("mailbox_id")
        if not mailbox_id:
            raise ValueError("mailbox_id missing from payload")
        return HistoryItem(
            mailbox_id=str(mailbox_id),
            history_id=data.get("history_id"),
        )


class GmailHistoryWorker:
    def __init__(
        self,
        redis_url: Optional[str] = None,
        concurrency: int = 3,
        manage_signals: bool = True,
    ):
        self.redis_url = redis_url or settings.REDIS_URL
        self.concurrency = max(concurrency, 1)
        self.manage_signals = manage_signals
        self.redis: Optional[redis.Redis] = None
        self.running = False
        self._shutdown = asyncio.Event()
        self._semaphore = asyncio.Semaphore(self.concurrency)

    async def start(self) -> None:
        logger.info(
            "Starting GmailHistoryWorker (queue=%s, concurrency=%d)",
            GMAIL_HISTORY_QUEUE,
            self.concurrency,
        )
        if self.manage_signals:
            try:
                signal.signal(signal.SIGINT, self._on_signal)
                signal.signal(signal.SIGTERM, self._on_signal)
            except Exception:
                pass

        self.redis = await redis.from_url(
            self.redis_url, encoding="utf-8", decode_responses=True
        )
        self.running = True

        workers = [
            asyncio.create_task(self._poll_loop()) for _ in range(self.concurrency)
        ]

        try:
            await self._shutdown.wait()
        finally:
            self.running = False
            for task in workers:
                task.cancel()
            await asyncio.gather(*workers, return_exceptions=True)
            if self.redis:
                await self.redis.aclose()
            logger.info("GmailHistoryWorker stopped")

    def _on_signal(self, signum, frame):
        logger.info("Received signal %s; shutting down gmail worker...", signum)
        self._shutdown.set()

    def stop(self) -> None:
        self._shutdown.set()

    async def _poll_loop(self) -> None:
        assert self.redis is not None
        while self.running:
            try:
                item = await self.redis.brpop(GMAIL_HISTORY_QUEUE, timeout=5)
                if not item:
                    continue
                _, value = item
                try:
                    payload = HistoryItem.from_json(value)
                except Exception as exc:
                    logger.error("Dropping invalid gmail queue payload: %s", exc)
                    continue

                async with self._semaphore:
                    await self._process(payload)
            except asyncio.CancelledError:
                break
            except Exception as exc:
                logger.error("Gmail history worker loop error: %s", exc)
                await asyncio.sleep(1)

    async def _process(self, payload: HistoryItem) -> None:
        try:
            result = await process_gmail_history_delta(
                payload.mailbox_id, payload.history_id
            )
            logger.info(
                "Gmail history processed mailbox=%s processed=%s matched=%s",
                payload.mailbox_id,
                result.get("processed"),
                result.get("matched"),
            )
        except Exception as exc:
            logger.error("Gmail history processing failed: %s", exc)


async def run_worker() -> None:
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    )
    worker = GmailHistoryWorker(
        concurrency=int(os.getenv("GMAIL_HISTORY_WORKER_CONCURRENCY", "3"))
    )
    await worker.start()


if __name__ == "__main__":
    asyncio.run(run_worker())
