"""Federation page data service — provides serialized dicts for .sx defpages."""
from __future__ import annotations


def _serialize_actor(actor) -> dict | None:
    if not actor:
        return None
    return {
        "id": actor.id,
        "preferred_username": actor.preferred_username,
        "display_name": getattr(actor, "display_name", None),
        "icon_url": getattr(actor, "icon_url", None),
        "summary": getattr(actor, "summary", None),
        "actor_url": getattr(actor, "actor_url", ""),
        "domain": getattr(actor, "domain", ""),
    }


def _serialize_timeline_item(item) -> dict:
    published = getattr(item, "published", None)
    return {
        "object_id": getattr(item, "object_id", "") or "",
        "author_inbox": getattr(item, "author_inbox", "") or "",
        "actor_icon": getattr(item, "actor_icon", None),
        "actor_name": getattr(item, "actor_name", "?"),
        "actor_username": getattr(item, "actor_username", ""),
        "actor_domain": getattr(item, "actor_domain", ""),
        "content": getattr(item, "content", ""),
        "summary": getattr(item, "summary", None),
        "published": published.strftime("%b %d, %H:%M") if published else "",
        "before_cursor": published.isoformat() if published else "",
        "url": getattr(item, "url", None),
        "post_type": getattr(item, "post_type", ""),
        "boosted_by": getattr(item, "boosted_by", None),
        "like_count": getattr(item, "like_count", 0) or 0,
        "boost_count": getattr(item, "boost_count", 0) or 0,
        "liked_by_me": getattr(item, "liked_by_me", False),
        "boosted_by_me": getattr(item, "boosted_by_me", False),
    }


def _serialize_remote_actor(a) -> dict:
    return {
        "id": getattr(a, "id", None),
        "display_name": getattr(a, "display_name", None) or getattr(a, "preferred_username", ""),
        "preferred_username": getattr(a, "preferred_username", ""),
        "domain": getattr(a, "domain", ""),
        "icon_url": getattr(a, "icon_url", None),
        "actor_url": getattr(a, "actor_url", ""),
        "summary": getattr(a, "summary", None),
    }


def _get_actor():
    from quart import g
    return getattr(g, "_social_actor", None)


def _require_actor():
    from quart import abort
    actor = _get_actor()
    if not actor:
        abort(403, "You need to choose a federation username first")
    return actor


class FederationPageService:
    """Service for federation page data, callable via (service "federation-page" ...)."""

    async def home_timeline_data(self, session, **kw):
        actor = _require_actor()
        from shared.services.registry import services
        items = await services.federation.get_home_timeline(session, actor.id)
        return {
            "items": [_serialize_timeline_item(i) for i in items],
            "timeline_type": "home",
            "actor": _serialize_actor(actor),
        }

    async def public_timeline_data(self, session, **kw):
        actor = _get_actor()
        from shared.services.registry import services
        items = await services.federation.get_public_timeline(session)
        return {
            "items": [_serialize_timeline_item(i) for i in items],
            "timeline_type": "public",
            "actor": _serialize_actor(actor),
        }

    async def compose_data(self, session, **kw):
        from quart import request
        _require_actor()
        reply_to = request.args.get("reply_to")
        return {"reply_to": reply_to or None}

    async def search_data(self, session, **kw):
        from quart import request
        actor = _get_actor()
        from shared.services.registry import services
        query = request.args.get("q", "").strip()
        actors_list = []
        total = 0
        followed_urls: list[str] = []
        if query:
            actors_list, total = await services.federation.search_actors(session, query)
            if actor:
                following, _ = await services.federation.get_following(
                    session, actor.preferred_username, page=1, per_page=1000,
                )
                followed_urls = [a.actor_url for a in following]
        return {
            "query": query,
            "actors": [_serialize_remote_actor(a) for a in actors_list],
            "total": total,
            "followed_urls": followed_urls,
            "actor": _serialize_actor(actor),
        }

    async def following_data(self, session, **kw):
        actor = _require_actor()
        from shared.services.registry import services
        actors_list, total = await services.federation.get_following(
            session, actor.preferred_username,
        )
        return {
            "actors": [_serialize_remote_actor(a) for a in actors_list],
            "total": total,
            "actor": _serialize_actor(actor),
        }

    async def followers_data(self, session, **kw):
        actor = _require_actor()
        from shared.services.registry import services
        actors_list, total = await services.federation.get_followers_paginated(
            session, actor.preferred_username,
        )
        following, _ = await services.federation.get_following(
            session, actor.preferred_username, page=1, per_page=1000,
        )
        followed_urls = [a.actor_url for a in following]
        return {
            "actors": [_serialize_remote_actor(a) for a in actors_list],
            "total": total,
            "followed_urls": followed_urls,
            "actor": _serialize_actor(actor),
        }

    async def actor_timeline_data(self, session, *, id=None, **kw):
        from quart import abort
        from sqlalchemy import select as sa_select
        from shared.models.federation import RemoteActor
        from shared.services.registry import services
        from shared.services.federation_impl import _remote_actor_to_dto

        actor = _get_actor()
        actor_id = id
        remote = (
            await session.execute(
                sa_select(RemoteActor).where(RemoteActor.id == actor_id)
            )
        ).scalar_one_or_none()
        if not remote:
            abort(404)
        remote_dto = _remote_actor_to_dto(remote)
        items = await services.federation.get_actor_timeline(session, actor_id)
        is_following = False
        if actor:
            from shared.models.federation import APFollowing
            existing = (
                await session.execute(
                    sa_select(APFollowing).where(
                        APFollowing.actor_profile_id == actor.id,
                        APFollowing.remote_actor_id == actor_id,
                    )
                )
            ).scalar_one_or_none()
            is_following = existing is not None
        return {
            "remote_actor": _serialize_remote_actor(remote_dto),
            "items": [_serialize_timeline_item(i) for i in items],
            "is_following": is_following,
            "actor": _serialize_actor(actor),
        }

    async def notifications_data(self, session, **kw):
        actor = _require_actor()
        from shared.services.registry import services
        items = await services.federation.get_notifications(session, actor.id)
        await services.federation.mark_notifications_read(session, actor.id)

        notif_dicts = []
        for n in items:
            created = getattr(n, "created_at", None)
            notif_dicts.append({
                "from_actor_name": getattr(n, "from_actor_name", "?"),
                "from_actor_username": getattr(n, "from_actor_username", ""),
                "from_actor_domain": getattr(n, "from_actor_domain", ""),
                "from_actor_icon": getattr(n, "from_actor_icon", None),
                "notification_type": getattr(n, "notification_type", ""),
                "target_content_preview": getattr(n, "target_content_preview", None),
                "created_at_formatted": created.strftime("%b %d, %H:%M") if created else "",
                "read": getattr(n, "read", True),
                "app_domain": getattr(n, "app_domain", ""),
            })
        return {"notifications": notif_dicts}
