"""The data update coordinator for OctoPrint."""

from __future__ import annotations

from datetime import timedelta
import logging
from typing import cast

from pyoctoprintapi import ApiError, OctoprintClient, PrinterOffline
from pyoctoprintapi.exceptions import UnauthorizedException
from yarl import URL

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PATH, CONF_PORT, CONF_SSL
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
import homeassistant.util.dt as dt_util

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


class OctoprintDataUpdateCoordinator(DataUpdateCoordinator):
    """Class to manage fetching Octoprint data."""

    config_entry: ConfigEntry

    def __init__(
        self,
        hass: HomeAssistant,
        octoprint: OctoprintClient,
        config_entry: ConfigEntry,
        interval: int,
    ) -> None:
        """Initialize."""
        super().__init__(
            hass,
            _LOGGER,
            name=f"octoprint-{config_entry.entry_id}",
            update_interval=timedelta(seconds=interval),
        )
        self.config_entry = config_entry
        self._octoprint = octoprint
        self._printer_offline = False
        self.data = {"printer": None, "job": None, "last_read_time": None}

    async def _async_update_data(self):
        """Update data via API."""
        printer = None
        try:
            job = await self._octoprint.get_job_info()
        except UnauthorizedException as err:
            raise ConfigEntryAuthFailed from err
        except ApiError as err:
            raise UpdateFailed(err) from err

        # If octoprint is on, but the printer is disconnected
        # printer will return a 409, so continue using the last
        # reading if there is one
        try:
            printer = await self._octoprint.get_printer_info()
        except PrinterOffline:
            if not self._printer_offline:
                _LOGGER.debug("Unable to retrieve printer information: Printer offline")
                self._printer_offline = True
        except UnauthorizedException as err:
            raise ConfigEntryAuthFailed from err
        except ApiError as err:
            raise UpdateFailed(err) from err
        else:
            self._printer_offline = False

        return {"job": job, "printer": printer, "last_read_time": dt_util.utcnow()}

    @property
    def device_info(self) -> DeviceInfo:
        """Device info."""
        unique_id = cast(str, self.config_entry.unique_id)
        configuration_url = URL.build(
            scheme=self.config_entry.data[CONF_SSL] and "https" or "http",
            host=self.config_entry.data[CONF_HOST],
            port=self.config_entry.data[CONF_PORT],
            path=self.config_entry.data[CONF_PATH],
        )

        return DeviceInfo(
            identifiers={(DOMAIN, unique_id)},
            manufacturer="OctoPrint",
            name="OctoPrint",
            configuration_url=str(configuration_url),
        )