import typing

from slack_sdk.web import SlackResponse

from apps.slack.constants import SLACK_RATE_LIMIT_DELAY


class UnexpectedResponse(typing.TypedDict):
    status: int
    headers: dict[str, typing.Any]
    body: str


class SlackAPIError(Exception):
    """
    Base class for Slack API errors. To add a new error class, add a new subclass of SlackAPIError in this file.
    See get_error_class at the end of this file for more details on how these are raised.
    """

    errors: tuple[str, ...]

    def __init__(self, response: UnexpectedResponse | SlackResponse):
        super().__init__(f"Slack API error! Response: {response}")
        self.response = response


class SlackAPIServerError(SlackAPIError):
    errors = ("internal_error", "fatal_error")


class SlackAPITokenError(SlackAPIError):
    errors = ("account_inactive", "token_revoked")


class SlackAPIChannelArchivedError(SlackAPIError):
    errors = ("is_archived",)


class SlackAPIRatelimitError(SlackAPIError):
    errors = ("ratelimited", "rate_limited", "message_limit_exceeded")

    def __init__(self, response: SlackResponse):
        super().__init__(response)
        self.retry_after = int(response.headers.get("Retry-After", SLACK_RATE_LIMIT_DELAY))


class SlackAPIPlanUpgradeRequiredError(SlackAPIError):
    errors = ("plan_upgrade_required",)


class SlackAPIInvalidAuthError(SlackAPIError):
    errors = ("invalid_auth",)


class SlackAPIUsergroupNotFoundError(SlackAPIError):
    errors = ("no_such_subteam", "subteam_not_found")


class SlackAPIUsergroupPaidTeamOnlyError(SlackAPIError):
    """
    https://api.slack.com/methods/usergroups.create#:~:text=Name%20too%20long.-,paid_teams_only,-Usergroups%20can%20only
    https://slack.com/help/articles/212906697-Create-a-user-group
    """

    errors = ("paid_teams_only",)


class SlackAPIInvalidUsersError(SlackAPIError):
    errors = ("invalid_users",)


class SlackAPIChannelNotFoundError(SlackAPIError):
    errors = ("channel_not_found",)


class SlackAPIMessageNotFoundError(SlackAPIError):
    errors = ("message_not_found",)


class SlackAPICantUpdateMessageError(SlackAPIError):
    errors = ("cant_update_message",)


class SlackAPIUserNotFoundError(SlackAPIError):
    errors = ("user_not_found",)


class SlackAPIChannelInactiveError(SlackAPIError):
    errors = ("is_inactive",)


class SlackAPIRestrictedActionError(SlackAPIError):
    errors = ("restricted_action",)


class SlackAPIPermissionDeniedError(SlackAPIError):
    errors = ("permission_denied",)


class SlackAPIFetchMembersFailedError(SlackAPIError):
    errors = ("fetch_members_failed",)


class SlackAPIViewNotFoundError(SlackAPIError):
    errors = ("not_found",)


class SlackAPICannotDMBotError(SlackAPIError):
    errors = ("cannot_dm_bot",)


class SlackAPIMethodNotSupportedForChannelTypeError(SlackAPIError):
    errors = ("method_not_supported_for_channel_type",)


_error_to_error_class = {
    error: error_class for error_class in SlackAPIError.__subclasses__() for error in error_class.errors
}


def get_error_class(response: UnexpectedResponse | SlackResponse) -> typing.Type[SlackAPIError]:
    """Get an appropriate error class for the response"""

    if isinstance(response, dict):  # UnexpectedResponse
        return SlackAPIServerError

    return _error_to_error_class.get(response["error"], SlackAPIError)
