"""Support for monitoring OctoPrint binary sensors."""
from __future__ import annotations

from abc import abstractmethod

from pyoctoprintapi import OctoprintPrinterInfo

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import OctoprintDataUpdateCoordinator
from .const import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up the available OctoPrint binary sensors."""
    coordinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN][
        config_entry.entry_id
    ]["coordinator"]
    device_id = config_entry.unique_id

    assert device_id is not None

    entities: list[BinarySensorEntity] = [
        OctoPrintPrintingBinarySensor(coordinator, device_id),
        OctoPrintPrintingErrorBinarySensor(coordinator, device_id),
    ]

    async_add_entities(entities)


class OctoPrintBinarySensorBase(
    CoordinatorEntity[OctoprintDataUpdateCoordinator], BinarySensorEntity
):
    """Representation an OctoPrint binary sensor."""

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        sensor_type: str,
        device_id: str,
    ) -> None:
        """Initialize a new OctoPrint sensor."""
        super().__init__(coordinator)
        self._device_id = device_id
        self._attr_name = f"OctoPrint {sensor_type}"
        self._attr_unique_id = f"{sensor_type}-{device_id}"

    @property
    def device_info(self):
        """Device info."""
        return self.coordinator.device_info

    @property
    def is_on(self):
        """Return true if binary sensor is on."""
        if not (printer := self.coordinator.data["printer"]):
            return None

        return bool(self._get_flag_state(printer))

    @property
    def available(self) -> bool:
        """Return if entity is available."""
        return self.coordinator.last_update_success and self.coordinator.data["printer"]

    @abstractmethod
    def _get_flag_state(self, printer_info: OctoprintPrinterInfo) -> bool | None:
        """Return the value of the sensor flag."""


class OctoPrintPrintingBinarySensor(OctoPrintBinarySensorBase):
    """Representation an OctoPrint binary sensor."""

    def __init__(
        self, coordinator: OctoprintDataUpdateCoordinator, device_id: str
    ) -> None:
        """Initialize a new OctoPrint sensor."""
        super().__init__(coordinator, "Printing", device_id)

    def _get_flag_state(self, printer_info: OctoprintPrinterInfo) -> bool | None:
        return bool(printer_info.state.flags.printing)


class OctoPrintPrintingErrorBinarySensor(OctoPrintBinarySensorBase):
    """Representation an OctoPrint binary sensor."""

    def __init__(
        self, coordinator: OctoprintDataUpdateCoordinator, device_id: str
    ) -> None:
        """Initialize a new OctoPrint sensor."""
        super().__init__(coordinator, "Printing Error", device_id)

    def _get_flag_state(self, printer_info: OctoprintPrinterInfo) -> bool | None:
        return bool(printer_info.state.flags.error)
