"""Inline federation publication — called at write time, not via async handler.

Replaces the old pattern where emit_event("post.published") → async handler →
publish_activity().  Now the originating service calls try_publish() directly,
which creates the APActivity in the same DB transaction.  AP delivery
(federation.activity_created → inbox POST) stays async.
"""
from __future__ import annotations

import logging
import os

from sqlalchemy.ext.asyncio import AsyncSession

from shared.services.registry import services

log = logging.getLogger(__name__)


async def try_publish(
    session: AsyncSession,
    *,
    user_id: int | None,
    activity_type: str,
    object_type: str,
    object_data: dict,
    source_type: str,
    source_id: int,
) -> None:
    """Publish an AP activity if federation is available and user has a profile.

    Safe to call from any app — returns silently if federation isn't wired
    or the user has no actor profile.
    """
    if not services.has("federation"):
        return

    if not user_id:
        return

    actor = await services.federation.get_actor_by_user_id(session, user_id)
    if not actor:
        return

    # Dedup: don't re-Create if already published, don't re-Delete if already deleted
    existing = await services.federation.get_activity_for_source(
        session, source_type, source_id,
    )
    if existing:
        if activity_type == "Create" and existing.activity_type != "Delete":
            return  # already published (allow re-Create after Delete/unpublish)
        if activity_type == "Update" and existing.activity_type == "Update":
            return  # already updated (Ghost fires duplicate webhooks)
        if activity_type == "Delete" and existing.activity_type == "Delete":
            return  # already deleted
    elif activity_type in ("Delete", "Update"):
        return  # never published, nothing to delete/update

    # Stable object ID: same source always gets the same object id so
    # Mastodon treats Create/Update/Delete as the same post.
    domain = os.getenv("AP_DOMAIN", "rose-ash.com")
    object_data["id"] = (
        f"https://{domain}/users/{actor.preferred_username}"
        f"/objects/{source_type.lower()}/{source_id}"
    )

    try:
        await services.federation.publish_activity(
            session,
            actor_user_id=user_id,
            activity_type=activity_type,
            object_type=object_type,
            object_data=object_data,
            source_type=source_type,
            source_id=source_id,
        )
        log.info(
            "Published %s/%s for %s#%d by user %d",
            activity_type, object_type, source_type, source_id, user_id,
        )
    except Exception:
        log.exception("Failed to publish activity for %s#%d", source_type, source_id)
