from http import HTTPStatus
from typing import Annotated, override

from fastapi import HTTPException, Security
from fastapi.responses import JSONResponse

from phederation.api.routes.base import BaseRoute, admin_route
from phederation.federation.instance import InstanceCollectionType
from phederation.models.activities import APActivity, ErrorMessageActivityValidation
from phederation.models.actors import ErrorMessageActorUnauthorized
from phederation.models.collections import APOrderedCollection, APOrderedCollectionPage
from phederation.security.authentication import UserInfo
from phederation.utils import ActivityType
from phederation.utils.base import ObjectId, UrlType
from phederation.utils.exceptions import ActivityPubException, DeliveryError


class AdminInstanceRoute(BaseRoute):

    SUPPORTED_TYPES: list[ActivityType] = [ActivityType.MAINTENANCE, ActivityType.DELETE]

    @override
    def setup(self):

        @admin_route(self)
        @self.router.get(f"/{UrlType.Instance.value}/{{collection}}", tags=["admin"])
        async def get_instance_collections(  # pyright: ignore[reportUnusedFunction]
            collection: InstanceCollectionType, page: int = 1
        ) -> APOrderedCollection | APOrderedCollectionPage:
            """Get special instance collections, like federated/blocked instances.

            Only possible in admin mode.
            """
            instance_actor_id = self.server.instance.actor_id
            collection_id: ObjectId | None = None
            if collection == InstanceCollectionType.Federated:
                collection_id = self.server.instance.federated_instances
            elif collection == InstanceCollectionType.Blocked:
                collection_id = self.server.instance.blocked_instances
            elif collection == InstanceCollectionType.BannedActors:
                collection_id = self.server.instance.banned_actors
            elif collection == InstanceCollectionType.FederatedSharedInboxes:
                collection_id = self.server.instance.federated_instance_actors
            elif collection == InstanceCollectionType.MaintenanceCommands:
                collection_id = self.server.instance.maintenance_commands
            elif collection == InstanceCollectionType.ActivitiesIncoming:
                collection_id = self.server.instance.activities_incoming
            elif collection == InstanceCollectionType.ActivitiesOutgoing:
                collection_id = self.server.instance.activities_outgoing

            if collection_id:
                # TODO: if the second page or more are accessed then the collection ID is not the admin id anymore - fix
                collection_obj = await self.server.handle_pagination(
                    collection_id=collection_id, actor_id=instance_actor_id, actor_for_authorization=instance_actor_id, page=page
                )
                return collection_obj
            else:
                raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=f"Collection {collection} does not exist")

        @self.router.post(
            "/maintenance",
            tags=["admin"],
            responses={
                HTTPStatus.BAD_REQUEST: {
                    "model": ErrorMessageActivityValidation,
                    "description": ErrorMessageActivityValidation().description,
                },
                HTTPStatus.UNAUTHORIZED: {
                    "model": ErrorMessageActorUnauthorized,
                    "description": ErrorMessageActorUnauthorized().description,
                },
            },
        )
        async def post_admin_maintenance(  # pyright: ignore[reportUnusedFunction]
            activity: APActivity,
            user_info: Annotated[UserInfo, Security(self.middleware.authorization, scopes=["admin"])],
        ):
            """
            Handle admin maintenance requests (client-to-server).

            The POST is secured by authentication ("admin" scope), the admin user need to authenticate using Bearer token (Oauth2).
            """
            # set actor who this was posted to as the owner of the activity.
            activity.actor = user_info.actor_id

            if not ActivityType(activity.type) in AdminInstanceRoute.SUPPORTED_TYPES:
                return JSONResponse(
                    content={
                        "Status": "Error",
                        "description": f"Activity is not in {AdminInstanceRoute.SUPPORTED_TYPES}, cannot handle in /maintenance route",
                    },
                    status_code=HTTPStatus.BAD_REQUEST,
                    media_type="application/activity+json",
                )

            try:
                activity = await self.server.process_outbox_activity(activity=activity, is_maintenance=True)
                if not activity.id:
                    raise DeliveryError("Activity id is not set")
            except ActivityPubException as e:
                self.logger.debug(f"Handle outbox failed with {type(e).__name__} {e.message}. Failing silently (HttpStatus.CONTINUE).")
                e.status_code = HTTPStatus.CONTINUE
                raise e

            # Servers MUST return a 201 Created HTTP code, and unless the activity is transient, MUST include the new id in the Location header.
            # https://www.w3.org/TR/activitypub/#client-to-server-interactions
            return JSONResponse(
                content={"Status": "OK"},
                status_code=HTTPStatus.CREATED,
                media_type="application/activity+json",
                headers={"Location": activity.id},
            )
