"""Utility functions for minimal_activitypub."""

import logging
from json import JSONDecodeError

from httpx import HTTPError
from httpx import Response
from whenever import Instant

from minimal_activitypub import __display_name__
from minimal_activitypub.constants import STATUS_BAD_REQUEST
from minimal_activitypub.constants import STATUS_CONFLICT
from minimal_activitypub.constants import STATUS_FORBIDDEN
from minimal_activitypub.constants import STATUS_GONE
from minimal_activitypub.constants import STATUS_INTERNAL_SERVER_ERROR
from minimal_activitypub.constants import STATUS_NOT_FOUND
from minimal_activitypub.constants import STATUS_TOO_MANY_REQUESTS
from minimal_activitypub.constants import STATUS_UNAUTHORIZED
from minimal_activitypub.constants import STATUS_UNPROCESSABLE_ENTITY
from minimal_activitypub.exceptions import ClientError
from minimal_activitypub.exceptions import ConflictError
from minimal_activitypub.exceptions import ForbiddenError
from minimal_activitypub.exceptions import GoneError
from minimal_activitypub.exceptions import NotFoundError
from minimal_activitypub.exceptions import RatelimitError
from minimal_activitypub.exceptions import ServerError
from minimal_activitypub.exceptions import UnauthorizedError
from minimal_activitypub.exceptions import UnprocessedError

logger = logging.getLogger(__display_name__)


def parse_rate_limit_reset(time_value: str | None) -> Instant | None:
    """Determine date and time when rate limit will be reset.

    :param time_value: The rate limit reset header value (Unix timestamp or ISO 8601 string)
    :return: An Instant representing the reset time, or None if unparseable
    """
    if not time_value:
        return None
    try:
        timestamp = int(time_value)
        return Instant.from_timestamp(timestamp)
    except (ValueError, TypeError):
        pass
    try:
        return Instant.parse_iso(time_value)
    except (ValueError, TypeError):
        pass
    return None


async def _determine_error_message(response: Response) -> str:
    """Extract error message from response JSON, falling back to response text.

    :param response: The HTTP response
    :return: The error message as a string
    """
    error_message = "Exception has occurred"
    try:
        content = response.json()
        error_message = content["error"]
    except (HTTPError, KeyError, JSONDecodeError):
        try:
            error_message = response.text
        except (HTTPError, LookupError) as lookup_error:
            logger.debug(
                "_determine_error_message - Error when determining response.text = %s",
                lookup_error,
            )
    logger.debug("_determine_error_message - error_message = %s", error_message)
    return error_message


async def _check_exception(response: Response) -> None:
    """Raise an appropriate exception based on HTTP response status code.

    :param response: The HTTP response to check
    """
    logger.debug("_check_exception - response.headers = %s", response.headers)
    logger.debug("_check_exception - response.status_code = %s", response.status_code)

    if response.status_code < STATUS_BAD_REQUEST:
        return

    error_message = await _determine_error_message(response)

    if response.status_code == STATUS_UNAUTHORIZED:
        raise UnauthorizedError(response.status_code, response.reason_phrase, error_message)
    if response.status_code == STATUS_FORBIDDEN:
        raise ForbiddenError(response.status_code, response.reason_phrase, error_message)
    if response.status_code == STATUS_NOT_FOUND:
        raise NotFoundError(response.status_code, response.reason_phrase, error_message)
    if response.status_code == STATUS_CONFLICT:
        raise ConflictError(response.status_code, response.reason_phrase, error_message)
    if response.status_code == STATUS_GONE:
        raise GoneError(response.status_code, response.reason_phrase, error_message)
    if response.status_code == STATUS_UNPROCESSABLE_ENTITY:
        raise UnprocessedError(response.status_code, response.reason_phrase, error_message)
    if response.status_code == STATUS_TOO_MANY_REQUESTS:
        raise RatelimitError(response.status_code, response.reason_phrase, error_message)
    if response.status_code < STATUS_INTERNAL_SERVER_ERROR:
        raise ClientError(response.status_code, response.reason_phrase, error_message)

    raise ServerError(response.status_code, response.reason_phrase, error_message)
