# retoor <retoor@molodetz.nl>

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

from devplacepy.attachments import get_attachments, link_attachments
from devplacepy.database import get_table, get_blocked_uids, get_users_by_uids
from devplacepy.templating import clear_messages_cache
from devplacepy.utils import (
    create_mention_notifications,
    create_notification,
    generate_uid,
    time_ago,
    track_action,
)
from devplacepy.services.audit import record as audit
from devplacepy.services.correction import schedule_correction
from devplacepy.services.ai_modifier import schedule_modification
from devplacepy.services.moderation.screening import (
    record as record_screening,
    refuse_if_blocked,
    screen_fields,
)

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

MAX_CONTENT_LENGTH = 2000


def _slim_attachment(attachment: dict[str, Any]) -> dict[str, Any]:
    return {
        "uid": attachment["uid"],
        "url": attachment["url"],
        "thumbnail_url": attachment.get("thumbnail_url"),
        "is_image": bool(attachment.get("is_image")),
        "is_video": bool(attachment.get("is_video")),
        "is_audio": bool(attachment.get("is_audio")),
        "original_filename": attachment.get("original_filename", ""),
        "file_size": attachment.get("file_size", 0),
        "mime_type": attachment.get("mime_type", ""),
    }


def message_frame(
    message: dict[str, Any], sender_username: str, client_id: Optional[str] = None,
    sender_role: Optional[str] = None, ai_processed: bool = False,
) -> dict[str, Any]:
    attachments = get_attachments("message", message["uid"])
    return {
        "type": "message",
        "uid": message["uid"],
        "sender_uid": message["sender_uid"],
        "sender_username": sender_username,
        "sender_role": sender_role,
        "receiver_uid": message["receiver_uid"],
        "content": message["content"],
        "created_at": message["created_at"],
        "time_ago": time_ago(message["created_at"]),
        "client_id": client_id,
        "attachments": [_slim_attachment(a) for a in attachments],
        "ai_processed": ai_processed,
    }


def persist_message(
    sender: dict[str, Any],
    receiver_uid: str,
    content: str,
    attachment_uids: Optional[list[str]] = None,
    *,
    request: Any = None,
    origin: str = "web",
) -> Optional[dict[str, Any]]:
    content = (content or "").strip()[:MAX_CONTENT_LENGTH]
    attachment_uids = attachment_uids or []
    if not content and not attachment_uids:
        return None

    receiver = get_table("users").find_one(uid=receiver_uid)
    if not receiver:
        return None

    if sender["uid"] in get_blocked_uids(receiver_uid):
        return None

    screening = screen_fields("messages", {"content": content})
    refuse_if_blocked(screening)

    sender_uid = sender["uid"]
    sender_username = sender.get("username", "")
    messages_table = get_table("messages")
    msg_uid = generate_uid()
    created_at = datetime.now(timezone.utc).isoformat()
    messages_table.insert(
        {
            "uid": msg_uid,
            "sender_uid": sender_uid,
            "receiver_uid": receiver_uid,
            "content": content,
            "read": False,
            "created_at": created_at,
            "updated_at": None,
        }
    )

    link_attachments(attachment_uids, "message", msg_uid)
    record_screening(
        screening,
        target_type="message",
        target_uid=msg_uid,
        actor_uid=sender_uid,
        request=request,
    )
    schedule_correction(sender, "messages", msg_uid, request)
    schedule_modification(sender, "messages", msg_uid, request)

    if sender_uid != receiver_uid:
        create_notification(
            receiver_uid,
            "message",
            f"{sender_username} sent you a message",
            sender_uid,
            f"/messages?with_uid={sender_uid}",
        )
        clear_messages_cache(receiver_uid)

    create_mention_notifications(
        content, sender_uid, f"/messages?with_uid={receiver_uid}"
    )
    track_action(sender_uid, "message")

    logger.info(
        "Message %s sent from %s to %s via %s",
        msg_uid,
        sender_username,
        receiver_uid,
        origin,
    )
    logger.debug("message %s content length=%d", msg_uid, len(content))

    summary = (
        f"{sender_username} sent a message to "
        f"{receiver.get('username') or receiver_uid}: {content}"
    )
    links = [
        audit.target("message", msg_uid),
        audit.recipient(receiver_uid, receiver.get("username")),
    ]
    if request is not None:
        audit.record(
            request,
            "message.send",
            user=sender,
            target_type="message",
            target_uid=msg_uid,
            summary=summary,
            metadata={"origin": origin},
            links=links,
        )
    else:
        audit.record_system(
            "message.send",
            actor_kind="user",
            actor_uid=sender_uid,
            actor_username=sender_username,
            actor_role="admin" if sender.get("role") == "Admin" else "member",
            origin=origin,
            target_type="message",
            target_uid=msg_uid,
            summary=summary,
            metadata={"origin": origin},
            links=links,
        )

    return {
        "uid": msg_uid,
        "sender_uid": sender_uid,
        "receiver_uid": receiver_uid,
        "content": content,
        "read": False,
        "created_at": created_at,
        "updated_at": None,
    }


def stamp_content_revision(message_uid: str) -> Optional[dict[str, Any]]:
    if not message_uid:
        return None
    table = get_table("messages")
    row = table.find_one(uid=message_uid)
    if not row:
        return None
    updated_at = datetime.now(timezone.utc).isoformat()
    table.update({"uid": message_uid, "updated_at": updated_at}, ["uid"])
    row["updated_at"] = updated_at
    return dict(row)


def push_content_revision(message_uid: str, *, ai_processed: bool = True) -> None:
    row = stamp_content_revision(message_uid)
    if not row:
        return
    sender = get_users_by_uids([row["sender_uid"]]).get(row["sender_uid"]) or {}
    frame = message_frame(
        row,
        sender.get("username", ""),
        sender_role=sender.get("role"),
        ai_processed=ai_processed,
    )
    targets = [row["sender_uid"], row["receiver_uid"]]
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = None
    if loop is not None:
        loop.create_task(_hub_send(targets, frame))
        return
    from devplacepy.services.background import background

    bg_loop = background.loop
    if bg_loop is not None:
        asyncio.run_coroutine_threadsafe(_hub_send(targets, frame), bg_loop)


async def _hub_send(user_uids: list[str], frame: dict[str, Any]) -> None:
    from devplacepy.services.messaging.hub import message_hub

    await message_hub.send_to_users(user_uids, frame)
