"""
Announce activity handler implementation.
"""

from typing import override

from phederation.models import dereference
from phederation.models.activities import APActivity
from phederation.models.objects import APObject
from phederation.utils import ObjectId
from phederation.utils.base import AccessType, collection_id_from_name
from phederation.utils.exceptions import HandlerError, ValidationError

from .base import ActivityHandler


class BlockHandler(ActivityHandler):
    """Handle Block actors and objects."""

    @override
    async def validate(self, activity: APActivity) -> APActivity:
        """Validate Block activity."""
        # Validate basic structure
        if activity.type != "Block":
            raise ValidationError("Invalid activity type")

        if not activity.object:
            raise ValidationError("Missing object")

        if not activity.actor:
            raise ValidationError("Missing actor")

        # Validate object exists
        object_id = activity.object.id if isinstance(activity.object, APObject) else activity.object
        obj = await self.resolver.resolve_object(object_id)
        if not obj:
            raise ValidationError(f"Object not found: {object_id}")

        # Validate actor can block
        actor = await self.resolver.resolve_actor(activity.actor)
        if not actor:
            raise ValidationError(f"Actor not found: {activity.actor}")

        return activity

    @override
    async def process_outbox(self, activity: APActivity) -> APActivity:
        """Process Block activity."""

        # Update object/actor blocks collection
        object_id = dereference(activity, key="object")
        actor_id = dereference(activity, key="actor")
        if not object_id or not actor_id:
            raise HandlerError(f"Block handler failed because object_id={object_id}, actor_id={actor_id}")
        # TODO: make blocks private by default, settings?
        access = AccessType.from_visibility(activity.visibility)
        await self._update_blocks_collection(object_id=object_id, actor_id=actor_id, access=access)

        return activity

    async def _update_blocks_collection(self, object_id: ObjectId, actor_id: ObjectId, access: AccessType) -> None:
        """Update object's blocks collection."""
        collection_blocks = collection_id_from_name(id=actor_id, name='blocks')
        _ = await self.collections.add_to_collection(collection_id=collection_blocks, items=object_id, access=access)

    @override
    async def process_inbox(self, activity: APActivity) -> None | str:
        # Update object/actor blocks collection, if required. The object will not be a local one, but we may want to update local copies of it
        object_id = dereference(activity, key="object")
        actor_id = dereference(activity, key="actor")
        if not object_id or not actor_id:
            raise HandlerError(f"Block handler inbox failed because object_id={object_id}, actor_id={actor_id}")
        access = AccessType.from_visibility(activity.visibility)
        await self._update_blocks_collection(object_id=object_id, actor_id=actor_id, access=access)
