from typing import Annotated, ClassVar, override

from fastapi import Query
from pydantic import BaseModel
from pydantic.config import ConfigDict

from phederation.api.routes.base import BaseRoute
from phederation.models import RelativeLink
from phederation.utils.base import actor_id_from_username
from phederation.utils.exceptions import InvalidURLError, UserError


class WebfingerReponse(BaseModel):
    """Full webfinger response for a given actor."""

    subject: str
    aliases: list[str]
    links: list[RelativeLink]

    model_config: ClassVar[ConfigDict] = ConfigDict(
        json_schema_extra={
            "examples": [
                {
                    "subject": f"acct:username@example.org",
                    "aliases": ["username@example.org", "@username@example.org"],
                    "links": [
                        {
                            "rel": "self",
                            "type": "application/activity+json",
                            "href": "username@example.org",
                        },
                        {
                            "rel": "http://webfinger.net/rel/profile-page",
                            "type": "text/html",
                            "href": "username@example.org",
                        },
                    ],
                }
            ]
        }
    )


class WebfingerRoute(BaseRoute):

    @override
    def setup(self):

        @self.app.get("/.well-known/webfinger", tags=["instance"])
        async def get_webfinger(  # pyright: ignore[reportUnusedFunction]
            resource: Annotated[str, Query(description="Resource parameter. Must be of the form 'acct:username@instanceurl'.")],
        ) -> WebfingerReponse:
            """Handle WebFinger requests."""
            if not resource:
                raise InvalidURLError("Missing resource parameter")
            if not resource.startswith("acct:"):
                raise InvalidURLError("Invalid resource parameter, must be 'acct:username@instance_url'")

            username_domain = resource.replace("acct:", "").split("@")
            if len(username_domain) < 2:
                self.logger.warning(f"Invalid resource parameter: {username_domain}")
                raise InvalidURLError("Invalid resource parameter, must be 'acct:username@instance_url'")

            username, domain = username_domain[0], username_domain[1]

            port: str = ""
            if ":" in domain:
                port = f":{domain.split(':')[1]}"

            if self.api.settings.domain.url == domain or self.api.settings.domain.netloc == domain:
                user_id = actor_id_from_username(
                    base_url=self.api.settings.domain.base_url,
                    username=username,
                )
            else:
                self.logger.warning(f"Wrong resource: url_with_port={self.api.settings.domain.netloc}; while domain={domain}, port={port}.")
                raise InvalidURLError("The 'resource' parameter must be 'acct:username@instance_url'. Instance_url does not match current instance.")

            if not await self.server.storage.actor.read(id=user_id):
                raise UserError(f"Webfinger: User not found: {user_id}")

            response = WebfingerReponse(
                subject=f"acct:{username}@{self.api.settings.domain.url}{port}",
                aliases=[user_id],
                links=[
                    RelativeLink(rel="self", type="application/activity+json", href=user_id),
                    RelativeLink(
                        rel="http://webfinger.net/rel/profile-page",
                        type="text/html",
                        href=user_id,  # TODO: change this to a proper HTML page
                    ),
                ],
            )
            return response
