"""
NotiLens Emitter — batches and delivers events to the NotiLens webhook.
"""

import json
import logging
import queue
import threading
import time
import urllib.request
import urllib.error
from typing import Optional

logger = logging.getLogger("notilens")

WEBHOOK_URL = "https://hook.notilens.com/webhook/{token}/send"


class NotiLensEmitter:
    def __init__(
        self,
        token: str,
        secret: str,
        agent: str,
        min_level: str = "info",
        batch_interval: float = 2.0,
        max_retries: int = 3,
        silent: bool = False,
    ):
        self.token = token
        self.secret = secret
        self.agent = agent
        self.silent = silent
        self.min_level = min_level.lower()
        self.url = WEBHOOK_URL.format(token=token)
        self._queue: queue.Queue = queue.Queue()
        self._stop = threading.Event()
        self._max_retries = max_retries
        self._batch_interval = batch_interval
        self._thread = threading.Thread(target=self._worker, daemon=True)
        self._thread.start()
        self._levels = {"debug": 0, "info": 1, "warning": 2, "error": 3, "critical": 4}

    # ── public API ───────────────────────────────────────────────────────────

    def send(
        self,
        event: str,
        message: str,
        level: str = "info",
        meta: Optional[dict] = None,
    ) -> None:
        """Queue an event for delivery. Non-blocking."""
        if self._level_num(level) < self._level_num(self.min_level):
            return
        payload = {
            "event": event,
            "message": message,
            "level": level,
            "agent": self.agent,
            "ts": time.time(),
            "meta": meta or {},
        }
        self._queue.put(payload)

    def flush(self, timeout: float = 5.0) -> None:
        """Block until queue is drained or timeout reached."""
        try:
            self._queue.join()
        except Exception:
            pass

    def shutdown(self) -> None:
        self.flush()
        self._stop.set()

    # ── internals ────────────────────────────────────────────────────────────

    def _level_num(self, level: str) -> int:
        return self._levels.get(level.lower(), 1)

    def _worker(self) -> None:
        while not self._stop.is_set():
            batch = []
            try:
                item = self._queue.get(timeout=self._batch_interval)
                batch.append(item)
                for _ in range(9):
                    try:
                        batch.append(self._queue.get_nowait())
                    except queue.Empty:
                        break
            except queue.Empty:
                continue

            for item in batch:
                self._deliver(item)
                self._queue.task_done()

    def _deliver(self, payload: dict) -> None:
        body = json.dumps(payload).encode()
        headers = {
            "Content-Type": "application/json",
            "X-NOTILENS-KEY": self.secret,
            "User-Agent": "NotiLens-SDK/0.2.0",
        }
        req = urllib.request.Request(self.url, data=body, headers=headers, method="POST")

        for attempt in range(1, self._max_retries + 1):
            try:
                with urllib.request.urlopen(req, timeout=5) as resp:
                    if resp.status < 300:
                        logger.debug("NotiLens delivered: %s", payload.get("event"))
                        return
            except urllib.error.HTTPError as e:
                logger.warning("NotiLens HTTP %s on attempt %d", e.code, attempt)
            except Exception as e:
                logger.warning("NotiLens delivery error attempt %d: %s", attempt, e)
            time.sleep(2 ** attempt)

        if not self.silent:
            logger.error("NotiLens: failed to deliver event after %d attempts", self._max_retries)
