# retoor <retoor@molodetz.nl>

import asyncio
import logging
from datetime import datetime, timezone
from typing import Optional

from devplacepy.database import db, get_users_by_uids
from devplacepy.services.messaging.hub import message_hub
from devplacepy.services.messaging.persist import message_frame

logger = logging.getLogger("messaging.relay")

POLL_INTERVAL_SECONDS = 1.0
BATCH_LIMIT = 500


class MessageRelay:
    def __init__(self) -> None:
        self._task: Optional[asyncio.Task] = None
        self._watermark: int = 0
        self._update_watermark: str = ""
        self._primed: bool = False

    def start(self) -> None:
        if self._task is not None and not self._task.done():
            return
        self._task = asyncio.create_task(self._run())
        logger.info("message relay started")

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

    async def _run(self) -> None:
        try:
            if not self._primed:
                self._watermark = self._max_id()
                self._update_watermark = datetime.now(timezone.utc).isoformat()
                self._primed = True
                logger.debug("message relay primed at watermark %d", self._watermark)
            while message_hub.has_connections():
                try:
                    await self._tick()
                    await self._tick_updates()
                except Exception:  # noqa: BLE001
                    logger.exception("message relay tick failed")
                await asyncio.sleep(POLL_INTERVAL_SECONDS)
        finally:
            self._task = None
            logger.info("message relay stopped (no active connections)")

    async def _tick(self) -> None:
        if "messages" not in db.tables:
            return
        rows = list(
            db.query(
                "SELECT * FROM messages WHERE id > :wm ORDER BY id ASC LIMIT :lim",
                wm=self._watermark,
                lim=BATCH_LIMIT,
            )
        )
        if not rows:
            return
        connected = message_hub.connected_user_uids()
        pending = [
            row
            for row in rows
            if (row["sender_uid"] in connected or row["receiver_uid"] in connected)
            and not message_hub.was_delivered(row["uid"])
        ]
        sender_uids = {row["sender_uid"] for row in pending}
        senders = get_users_by_uids(list(sender_uids)) if sender_uids else {}
        for row in pending:
            sender = senders.get(row["sender_uid"]) or {}
            frame = message_frame(dict(row), sender.get("username", ""), sender_role=sender.get("role"))
            message_hub.mark_delivered(row["uid"])
            await message_hub.send_to_users(
                [row["sender_uid"], row["receiver_uid"]], frame
            )
        self._watermark = max(self._watermark, max(row["id"] for row in rows))
        logger.debug(
            "message relay delivered %d of %d new rows, watermark %d",
            len(pending),
            len(rows),
            self._watermark,
        )

    async def _tick_updates(self) -> None:
        if "messages" not in db.tables or not self._update_watermark:
            return
        rows = list(
            db.query(
                "SELECT * FROM messages"
                " WHERE updated_at IS NOT NULL AND updated_at > :uwm"
                " ORDER BY updated_at ASC LIMIT :lim",
                uwm=self._update_watermark,
                lim=BATCH_LIMIT,
            )
        )
        if not rows:
            return
        connected = message_hub.connected_user_uids()
        pending = [
            row
            for row in rows
            if row["sender_uid"] in connected or row["receiver_uid"] in connected
        ]
        sender_uids = {row["sender_uid"] for row in pending}
        senders = get_users_by_uids(list(sender_uids)) if sender_uids else {}
        for row in pending:
            sender = senders.get(row["sender_uid"]) or {}
            frame = message_frame(
                dict(row),
                sender.get("username", ""),
                sender_role=sender.get("role"),
                ai_processed=True,
            )
            await message_hub.send_to_users(
                [row["sender_uid"], row["receiver_uid"]], frame
            )
        latest = max((row.get("updated_at") or "") for row in rows)
        if latest:
            self._update_watermark = latest
        logger.debug(
            "message relay pushed %d of %d content revisions",
            len(pending),
            len(rows),
        )


message_relay = MessageRelay()
