"""Support for RESTful API."""

from __future__ import annotations

import logging
from typing import Any

import aiohttp
from multidict import CIMultiDictProxy
import xmltodict

from homeassistant.core import HomeAssistant
from homeassistant.helpers import template
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.json import json_dumps
from homeassistant.util.ssl import SSLCipherList

from .const import XML_MIME_TYPES

DEFAULT_TIMEOUT = 10

_LOGGER = logging.getLogger(__name__)


class RestData:
    """Class for handling the data retrieval."""

    def __init__(
        self,
        hass: HomeAssistant,
        method: str,
        resource: str,
        encoding: str,
        auth: aiohttp.DigestAuthMiddleware | aiohttp.BasicAuth | tuple[str, str] | None,
        headers: dict[str, str] | None,
        params: dict[str, str] | None,
        data: str | None,
        verify_ssl: bool,
        ssl_cipher_list: str,
        timeout: int = DEFAULT_TIMEOUT,
    ) -> None:
        """Initialize the data object."""
        self._hass = hass
        self._method = method
        self._resource = resource
        self._encoding = encoding

        # Convert auth tuple to aiohttp.BasicAuth if needed
        if isinstance(auth, tuple) and len(auth) == 2:
            self._auth: aiohttp.BasicAuth | aiohttp.DigestAuthMiddleware | None = (
                aiohttp.BasicAuth(auth[0], auth[1])
            )
        else:
            self._auth = auth

        self._headers = headers
        self._params = params
        self._request_data = data
        self._timeout = aiohttp.ClientTimeout(total=timeout)
        self._verify_ssl = verify_ssl
        self._ssl_cipher_list = SSLCipherList(ssl_cipher_list)
        self._session: aiohttp.ClientSession | None = None
        self.data: str | None = None
        self.last_exception: Exception | None = None
        self.headers: CIMultiDictProxy[str] | None = None

    def set_payload(self, payload: str) -> None:
        """Set request data."""
        self._request_data = payload

    @property
    def url(self) -> str:
        """Get url."""
        return self._resource

    def set_url(self, url: str) -> None:
        """Set url."""
        self._resource = url

    def data_without_xml(self) -> str | None:
        """If the data is an XML string, convert it to a JSON string."""
        _LOGGER.debug("Data fetched from resource: %s", self.data)
        if (
            (value := self.data) is not None
            # If the http request failed, headers will be None
            and (headers := self.headers) is not None
            and (content_type := headers.get("content-type"))
            and content_type.startswith(XML_MIME_TYPES)
        ):
            value = json_dumps(xmltodict.parse(value))
            _LOGGER.debug("JSON converted from XML: %s", value)
        return value

    async def async_update(self, log_errors: bool = True) -> None:
        """Get the latest data from REST service with provided method."""
        if not self._session:
            self._session = async_get_clientsession(
                self._hass,
                verify_ssl=self._verify_ssl,
                ssl_cipher=self._ssl_cipher_list,
            )

        rendered_headers = template.render_complex(self._headers, parse_result=False)
        rendered_params = template.render_complex(self._params)

        _LOGGER.debug("Updating from %s", self._resource)
        # Create request kwargs
        request_kwargs: dict[str, Any] = {
            "headers": rendered_headers,
            "params": rendered_params,
            "timeout": self._timeout,
        }

        # Handle authentication
        if isinstance(self._auth, aiohttp.BasicAuth):
            request_kwargs["auth"] = self._auth
        elif isinstance(self._auth, aiohttp.DigestAuthMiddleware):
            request_kwargs["middlewares"] = (self._auth,)

        # Handle data/content
        if self._request_data:
            request_kwargs["data"] = self._request_data
        try:
            # Make the request
            async with self._session.request(
                self._method, self._resource, **request_kwargs
            ) as response:
                # Read the response
                self.data = await response.text(encoding=self._encoding)
                self.headers = response.headers

        except TimeoutError as ex:
            if log_errors:
                _LOGGER.error("Timeout while fetching data: %s", self._resource)
            self.last_exception = ex
            self.data = None
            self.headers = None
        except aiohttp.ClientError as ex:
            if log_errors:
                _LOGGER.error(
                    "Error fetching data: %s failed with %s", self._resource, ex
                )
            self.last_exception = ex
            self.data = None
            self.headers = None
