"""Social fediverse routes: timeline, compose, search, follow, interactions, notifications."""
from __future__ import annotations

import logging
from datetime import datetime

from quart import Blueprint, request, g, redirect, url_for, abort, Response

from shared.services.registry import services
from shared.sx.helpers import sx_response

log = logging.getLogger(__name__)


def _require_actor():
    """Return actor context or abort 403."""
    actor = g.get("ctx", {}).get("actor") if hasattr(g, "ctx") else None
    if not actor:
        actor = getattr(g, "_social_actor", None)
    if not actor:
        abort(403, "You need to choose a federation username first")
    return actor


def register(url_prefix="/social"):
    bp = Blueprint("social", __name__, url_prefix=url_prefix)

    @bp.before_request
    async def load_actor():
        """Load actor profile for authenticated users."""
        if g.get("user"):
            actor = await services.federation.get_actor_by_user_id(g.s, g.user.id)
            g._social_actor = actor

    @bp.before_request
    async def _prepare_page_data():
        """Pre-render content for defpage routes."""
        endpoint = request.endpoint or ""

        if endpoint.endswith("defpage_home_timeline"):
            actor = _require_actor()
            items = await services.federation.get_home_timeline(g.s, actor.id)
            from sx.sx_components import _timeline_content_sx
            g.home_timeline_content = _timeline_content_sx(items, "home", actor)

        elif endpoint.endswith("defpage_public_timeline"):
            actor = getattr(g, "_social_actor", None)
            items = await services.federation.get_public_timeline(g.s)
            from sx.sx_components import _timeline_content_sx
            g.public_timeline_content = _timeline_content_sx(items, "public", actor)

        elif endpoint.endswith("defpage_compose_form"):
            actor = _require_actor()
            from sx.sx_components import _compose_content_sx
            reply_to = request.args.get("reply_to")
            g.compose_content = _compose_content_sx(actor, reply_to)

        elif endpoint.endswith("defpage_search"):
            actor = getattr(g, "_social_actor", None)
            query = request.args.get("q", "").strip()
            actors_list = []
            total = 0
            followed_urls: set[str] = set()
            if query:
                actors_list, total = await services.federation.search_actors(g.s, query)
                if actor:
                    following, _ = await services.federation.get_following(
                        g.s, actor.preferred_username, page=1, per_page=1000,
                    )
                    followed_urls = {a.actor_url for a in following}
            from sx.sx_components import _search_content_sx
            g.search_content = _search_content_sx(query, actors_list, total, 1, followed_urls, actor)

        elif endpoint.endswith("defpage_following_list"):
            actor = _require_actor()
            actors_list, total = await services.federation.get_following(
                g.s, actor.preferred_username,
            )
            from sx.sx_components import _following_content_sx
            g.following_content = _following_content_sx(actors_list, total, actor)

        elif endpoint.endswith("defpage_followers_list"):
            actor = _require_actor()
            actors_list, total = await services.federation.get_followers_paginated(
                g.s, actor.preferred_username,
            )
            following, _ = await services.federation.get_following(
                g.s, actor.preferred_username, page=1, per_page=1000,
            )
            followed_urls = {a.actor_url for a in following}
            from sx.sx_components import _followers_content_sx
            g.followers_content = _followers_content_sx(actors_list, total, followed_urls, actor)

        elif endpoint.endswith("defpage_actor_timeline"):
            actor = getattr(g, "_social_actor", None)
            actor_id = request.view_args.get("id")
            from shared.models.federation import RemoteActor
            from sqlalchemy import select as sa_select
            remote = (
                await g.s.execute(
                    sa_select(RemoteActor).where(RemoteActor.id == actor_id)
                )
            ).scalar_one_or_none()
            if not remote:
                abort(404)
            from shared.services.federation_impl import _remote_actor_to_dto
            remote_dto = _remote_actor_to_dto(remote)
            items = await services.federation.get_actor_timeline(g.s, actor_id)
            is_following = False
            if actor:
                from shared.models.federation import APFollowing
                existing = (
                    await g.s.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
            from sx.sx_components import _actor_timeline_content_sx
            g.actor_timeline_content = _actor_timeline_content_sx(remote_dto, items, is_following, actor)

        elif endpoint.endswith("defpage_notifications"):
            actor = _require_actor()
            items = await services.federation.get_notifications(g.s, actor.id)
            await services.federation.mark_notifications_read(g.s, actor.id)
            from sx.sx_components import _notifications_content_sx
            g.notifications_content = _notifications_content_sx(items)

    # -- Timeline pagination ---------------------------------------------------

    @bp.get("/timeline")
    async def home_timeline_page():
        actor = _require_actor()
        before_str = request.args.get("before")
        before = None
        if before_str:
            try:
                before = datetime.fromisoformat(before_str)
            except ValueError:
                pass
        items = await services.federation.get_home_timeline(
            g.s, actor.id, before=before,
        )
        from sx.sx_components import render_timeline_items
        sx_src = await render_timeline_items(items, "home", actor)
        return sx_response(sx_src)

    @bp.get("/public/timeline")
    async def public_timeline_page():
        before_str = request.args.get("before")
        before = None
        if before_str:
            try:
                before = datetime.fromisoformat(before_str)
            except ValueError:
                pass
        items = await services.federation.get_public_timeline(g.s, before=before)
        actor = getattr(g, "_social_actor", None)
        from sx.sx_components import render_timeline_items
        sx_src = await render_timeline_items(items, "public", actor)
        return sx_response(sx_src)

    # -- Compose ---------------------------------------------------------------

    @bp.post("/compose")
    async def compose_submit():
        actor = _require_actor()
        form = await request.form
        content = form.get("content", "").strip()
        if not content:
            return redirect(url_for("social.defpage_compose_form"))

        visibility = form.get("visibility", "public")
        in_reply_to = form.get("in_reply_to") or None

        await services.federation.create_local_post(
            g.s, actor.id,
            content=content,
            visibility=visibility,
            in_reply_to=in_reply_to,
        )
        return redirect(url_for("social.defpage_home_timeline"))

    @bp.post("/delete/<int:post_id>")
    async def delete_post(post_id: int):
        actor = _require_actor()
        await services.federation.delete_local_post(g.s, actor.id, post_id)
        return redirect(url_for("social.defpage_home_timeline"))

    # -- Search + Follow -------------------------------------------------------

    @bp.get("/search/page")
    async def search_page():
        actor = getattr(g, "_social_actor", None)
        query = request.args.get("q", "").strip()
        page = request.args.get("page", 1, type=int)
        actors_list = []
        total = 0
        followed_urls: set[str] = set()
        if query:
            actors_list, total = await services.federation.search_actors(
                g.s, query, page=page,
            )
            if actor:
                following, _ = await services.federation.get_following(
                    g.s, actor.preferred_username, page=1, per_page=1000,
                )
                followed_urls = {a.actor_url for a in following}
        from sx.sx_components import render_search_results
        sx_src = await render_search_results(actors_list, query, page, followed_urls, actor)
        return sx_response(sx_src)

    @bp.post("/follow")
    async def follow():
        actor = _require_actor()
        form = await request.form
        remote_actor_url = form.get("actor_url", "")
        if remote_actor_url:
            await services.federation.send_follow(
                g.s, actor.preferred_username, remote_actor_url,
            )
        if request.headers.get("SX-Request") or request.headers.get("HX-Request"):
            return await _actor_card_response(actor, remote_actor_url, is_followed=True)
        return redirect(request.referrer or url_for("social.defpage_search"))

    @bp.post("/unfollow")
    async def unfollow():
        actor = _require_actor()
        form = await request.form
        remote_actor_url = form.get("actor_url", "")
        if remote_actor_url:
            await services.federation.unfollow(
                g.s, actor.preferred_username, remote_actor_url,
            )
        if request.headers.get("SX-Request") or request.headers.get("HX-Request"):
            return await _actor_card_response(actor, remote_actor_url, is_followed=False)
        return redirect(request.referrer or url_for("social.defpage_search"))

    async def _actor_card_response(actor, remote_actor_url, is_followed):
        """Re-render a single actor card after follow/unfollow via HTMX."""
        remote_dto = await services.federation.get_or_fetch_remote_actor(
            g.s, remote_actor_url,
        )
        if not remote_dto:
            return Response("", status=200)
        followed_urls = {remote_actor_url} if is_followed else set()
        referer = request.referrer or ""
        if "/followers" in referer:
            list_type = "followers"
        else:
            list_type = "following"
        from sx.sx_components import render_actor_card
        return sx_response(render_actor_card(remote_dto, actor, followed_urls, list_type=list_type))

    # -- Interactions ----------------------------------------------------------

    @bp.post("/like")
    async def like():
        actor = _require_actor()
        form = await request.form
        object_id = form.get("object_id", "")
        author_inbox = form.get("author_inbox", "")
        await services.federation.like_post(g.s, actor.id, object_id, author_inbox)
        return await _interaction_buttons_response(actor, object_id, author_inbox)

    @bp.post("/unlike")
    async def unlike():
        actor = _require_actor()
        form = await request.form
        object_id = form.get("object_id", "")
        author_inbox = form.get("author_inbox", "")
        await services.federation.unlike_post(g.s, actor.id, object_id, author_inbox)
        return await _interaction_buttons_response(actor, object_id, author_inbox)

    @bp.post("/boost")
    async def boost():
        actor = _require_actor()
        form = await request.form
        object_id = form.get("object_id", "")
        author_inbox = form.get("author_inbox", "")
        await services.federation.boost_post(g.s, actor.id, object_id, author_inbox)
        return await _interaction_buttons_response(actor, object_id, author_inbox)

    @bp.post("/unboost")
    async def unboost():
        actor = _require_actor()
        form = await request.form
        object_id = form.get("object_id", "")
        author_inbox = form.get("author_inbox", "")
        await services.federation.unboost_post(g.s, actor.id, object_id, author_inbox)
        return await _interaction_buttons_response(actor, object_id, author_inbox)

    async def _interaction_buttons_response(actor, object_id, author_inbox):
        """Re-render interaction buttons after a like/boost action."""
        from shared.models.federation import APInteraction, APRemotePost, APActivity
        from sqlalchemy import select

        svc = services.federation
        post_type, post_id = await svc._resolve_post(g.s, object_id)

        like_count = 0
        boost_count = 0
        liked_by_me = False
        boosted_by_me = False

        if post_type:
            from sqlalchemy import func as sa_func
            like_count = (await g.s.execute(
                select(sa_func.count(APInteraction.id)).where(
                    APInteraction.post_type == post_type,
                    APInteraction.post_id == post_id,
                    APInteraction.interaction_type == "like",
                )
            )).scalar() or 0
            boost_count = (await g.s.execute(
                select(sa_func.count(APInteraction.id)).where(
                    APInteraction.post_type == post_type,
                    APInteraction.post_id == post_id,
                    APInteraction.interaction_type == "boost",
                )
            )).scalar() or 0
            liked_by_me = bool((await g.s.execute(
                select(APInteraction.id).where(
                    APInteraction.actor_profile_id == actor.id,
                    APInteraction.post_type == post_type,
                    APInteraction.post_id == post_id,
                    APInteraction.interaction_type == "like",
                ).limit(1)
            )).scalar())
            boosted_by_me = bool((await g.s.execute(
                select(APInteraction.id).where(
                    APInteraction.actor_profile_id == actor.id,
                    APInteraction.post_type == post_type,
                    APInteraction.post_id == post_id,
                    APInteraction.interaction_type == "boost",
                ).limit(1)
            )).scalar())

        from sx.sx_components import render_interaction_buttons
        return sx_response(render_interaction_buttons(
            object_id=object_id,
            author_inbox=author_inbox,
            like_count=like_count,
            boost_count=boost_count,
            liked_by_me=liked_by_me,
            boosted_by_me=boosted_by_me,
            actor=actor,
        ))

    # -- Following / Followers pagination --------------------------------------

    @bp.get("/following/page")
    async def following_list_page():
        actor = _require_actor()
        page = request.args.get("page", 1, type=int)
        actors_list, total = await services.federation.get_following(
            g.s, actor.preferred_username, page=page,
        )
        from sx.sx_components import render_following_items
        sx_src = await render_following_items(actors_list, page, actor)
        return sx_response(sx_src)

    @bp.get("/followers/page")
    async def followers_list_page():
        actor = _require_actor()
        page = request.args.get("page", 1, type=int)
        actors_list, total = await services.federation.get_followers_paginated(
            g.s, actor.preferred_username, page=page,
        )
        following, _ = await services.federation.get_following(
            g.s, actor.preferred_username, page=1, per_page=1000,
        )
        followed_urls = {a.actor_url for a in following}
        from sx.sx_components import render_followers_items
        sx_src = await render_followers_items(actors_list, page, followed_urls, actor)
        return sx_response(sx_src)

    @bp.get("/actor/<int:id>/timeline")
    async def actor_timeline_page(id: int):
        actor = getattr(g, "_social_actor", None)
        before_str = request.args.get("before")
        before = None
        if before_str:
            try:
                before = datetime.fromisoformat(before_str)
            except ValueError:
                pass
        items = await services.federation.get_actor_timeline(
            g.s, id, before=before,
        )
        from sx.sx_components import render_actor_timeline_items
        sx_src = await render_actor_timeline_items(items, id, actor)
        return sx_response(sx_src)

    # -- Notifications ---------------------------------------------------------

    @bp.get("/notifications/count")
    async def notification_count():
        actor = getattr(g, "_social_actor", None)
        if not actor:
            return Response("0", content_type="text/plain")
        count = await services.federation.unread_notification_count(g.s, actor.id)
        if count > 0:
            from shared.sx.jinja_bridge import render as render_comp
            return Response(
                render_comp("notification-badge", count=str(count)),
                content_type="text/html",
            )
        return Response("", content_type="text/html")

    @bp.post("/notifications/read")
    async def mark_read():
        actor = _require_actor()
        await services.federation.mark_notifications_read(g.s, actor.id)
        return redirect(url_for("social.defpage_notifications"))

    return bp
