# retoor <retoor@molodetz.nl>

import asyncio
import logging
from datetime import datetime, timezone
from devplacepy.database import (
    get_table,
    notification_enabled,
    get_silenced_uids,
)
from devplacepy.services.background import background
from devplacepy.utils.text import generate_uid, extract_mentions

logger = logging.getLogger(__name__)

PUSH_ICON = "/static/apple-touch-icon.png"
DEFAULT_PUSH_URL = "/notifications"

_push_tasks: set[asyncio.Task] = set()


async def _safe_notify(user_uid: str, payload: dict[str, str]) -> None:
    from devplacepy import push

    try:
        await push.notify_user(user_uid, payload)
    except Exception as e:
        logger.warning("Push delivery failed for %s: %s", user_uid, e)


def _schedule_push(user_uid: str, message: str, target_url: str | None) -> None:
    payload = {
        "title": "DevPlace",
        "message": message,
        "icon": PUSH_ICON,
        "url": target_url or DEFAULT_PUSH_URL,
    }
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = background.loop
        if loop is None:
            logger.warning("no event loop available for push delivery to %s", user_uid)
            return
        asyncio.run_coroutine_threadsafe(_safe_notify(user_uid, payload), loop)
        return
    task = loop.create_task(_safe_notify(user_uid, payload))
    _push_tasks.add(task)
    task.add_done_callback(_push_tasks.discard)


def _schedule_telegram(user_uid: str, message: str) -> None:
    from devplacepy.services.telegram import store as telegram_store

    try:
        telegram_store.enqueue_outbox(user_uid, message)
    except Exception as e:
        logger.warning("Telegram delivery enqueue failed for %s: %s", user_uid, e)


def create_notification(
    user_uid: str,
    notification_type: str,
    message: str,
    related_uid: str,
    target_url: str | None = None,
) -> None:
    background.submit(
        _deliver_notification,
        user_uid,
        notification_type,
        message,
        related_uid,
        target_url,
    )


def _deliver_notification(
    user_uid: str,
    notification_type: str,
    message: str,
    related_uid: str,
    target_url: str | None = None,
) -> None:
    from devplacepy.templating import clear_unread_cache

    if related_uid and related_uid in get_silenced_uids(user_uid):
        return

    in_app = notification_enabled(user_uid, notification_type, "in_app")
    push = notification_enabled(user_uid, notification_type, "push")
    telegram = notification_enabled(user_uid, notification_type, "telegram")
    if in_app:
        get_table("notifications").insert(
            {
                "uid": generate_uid(),
                "user_uid": user_uid,
                "type": notification_type,
                "message": message,
                "related_uid": related_uid,
                "target_url": target_url,
                "read": False,
                "created_at": datetime.now(timezone.utc).isoformat(),
            }
        )
        clear_unread_cache(user_uid)
    if push:
        _schedule_push(user_uid, message, target_url)
    if telegram:
        _schedule_telegram(user_uid, message)
    from devplacepy.services.audit import record as audit

    audit.record_system(
        "notification.create",
        actor_kind="system",
        target_type="notification",
        target_uid=related_uid,
        summary=f"notification created: {message}",
        metadata={
            "type": notification_type,
            "in_app": in_app,
            "push": push,
            "telegram": telegram,
        },
        links=[
            audit.recipient(user_uid),
            audit.link("source", "notification", related_uid),
        ],
    )


def create_thread_notifications(
    target_uid: str, actor_uid: str, comment_url: str, exclude_uids: set[str]
) -> None:
    background.submit(
        _deliver_thread_notifications, target_uid, actor_uid, comment_url, exclude_uids
    )


def _deliver_thread_notifications(
    target_uid: str, actor_uid: str, comment_url: str, exclude_uids: set[str]
) -> None:
    users = get_table("users")
    actor = users.find_one(uid=actor_uid)
    if not actor:
        return
    exclude = set(exclude_uids) | {actor_uid}
    comments = get_table("comments")
    participant_uids = {
        row["user_uid"]
        for row in comments.find(target_type="post", target_uid=target_uid, deleted_at=None)
    } - exclude
    for participant_uid in participant_uids:
        create_notification(
            participant_uid,
            "thread",
            f"{actor['username']} also commented on a post you commented on",
            actor_uid,
            comment_url,
        )


def create_mention_notifications(content: str, actor_uid: str, target_url: str) -> None:
    background.submit(_deliver_mention_notifications, content, actor_uid, target_url)


def _deliver_mention_notifications(
    content: str, actor_uid: str, target_url: str
) -> None:
    usernames = list(dict.fromkeys(extract_mentions(content)))
    if not usernames:
        return
    users = get_table("users")
    actor = users.find_one(uid=actor_uid)
    if not actor:
        return
    actor_username = actor["username"]
    for mentioned in users.find(users.table.columns.username.in_(usernames)):
        if mentioned["uid"] != actor_uid:
            create_notification(
                mentioned["uid"],
                "mention",
                f"@{actor_username} mentioned you",
                actor_uid,
                target_url,
            )
