# retoor <retoor@molodetz.nl>

from __future__ import annotations

from devplacepy.database import db
from devplacepy.services.base import BaseService
from devplacepy.services.pubsub import publish as pubsub_publish

BATCH_LIMIT = 500


class NotificationRelayService(BaseService):
    title = "Notification relay"
    description = (
        "Bridges newly persisted in-app notifications onto the pub/sub bus so the "
        "recipient's browser raises a live toast. Polls the notifications table on the "
        "service lock owner, where every pub/sub subscriber converges. A toast fires "
        "exactly when the in-app channel is enabled, since the row only exists then."
    )
    default_enabled = True

    def __init__(self):
        super().__init__(name="notification_relay", interval_seconds=1)
        self._watermark = 0
        self._primed = False

    def _max_id(self) -> int:
        if "notifications" not in db.tables:
            return 0
        rows = list(db.query("SELECT MAX(id) AS max_id FROM notifications"))
        value = rows[0]["max_id"] if rows else None
        return int(value or 0)

    def _counts(self, user_uid: str) -> dict:
        notifications = 0
        messages = 0
        if "notifications" in db.tables:
            rows = list(
                db.query(
                    "SELECT COUNT(*) AS c FROM notifications "
                    "WHERE user_uid = :u AND read = 0",
                    u=user_uid,
                )
            )
            notifications = int(rows[0]["c"]) if rows else 0
        if "messages" in db.tables:
            rows = list(
                db.query(
                    "SELECT COUNT(*) AS c FROM messages "
                    "WHERE receiver_uid = :u AND read = 0",
                    u=user_uid,
                )
            )
            messages = int(rows[0]["c"]) if rows else 0
        return {"notifications": notifications, "messages": messages}

    async def run_once(self) -> None:
        if "notifications" not in db.tables:
            return
        if not self._primed:
            self._watermark = self._max_id()
            self._primed = True
            return
        rows = list(
            db.query(
                "SELECT id, uid, user_uid, type, message, target_url "
                "FROM notifications WHERE id > :wm ORDER BY id ASC LIMIT :lim",
                wm=self._watermark,
                lim=BATCH_LIMIT,
            )
        )
        if not rows:
            return
        delivered = 0
        for row in rows:
            delivered += await pubsub_publish(
                f"user.{row['user_uid']}.notifications",
                {
                    "uid": row["uid"],
                    "type": row["type"],
                    "message": row["message"],
                    "target_url": row["target_url"],
                },
            )
        for user_uid in {row["user_uid"] for row in rows}:
            await pubsub_publish(f"user.{user_uid}.counts", self._counts(user_uid))
        self._watermark = max(row["id"] for row in rows)
        if delivered:
            self.log(f"relayed {len(rows)} notification(s), {delivered} live toast(s)")

    def collect_metrics(self) -> dict:
        return {"watermark": self._watermark, "primed": self._primed}
