"""Support for Octoprint buttons."""

from pyoctoprintapi import OctoprintClient, OctoprintPrinterInfo

from homeassistant.components.button import ButtonDeviceClass, ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
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 Octoprint control buttons."""
    coordinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN][
        config_entry.entry_id
    ]["coordinator"]
    client: OctoprintClient = hass.data[DOMAIN][config_entry.entry_id]["client"]
    device_id = config_entry.unique_id
    assert device_id is not None

    async_add_entities(
        [
            OctoprintResumeJobButton(coordinator, device_id, client),
            OctoprintPauseJobButton(coordinator, device_id, client),
            OctoprintStopJobButton(coordinator, device_id, client),
            OctoprintShutdownSystemButton(coordinator, device_id, client),
            OctoprintRebootSystemButton(coordinator, device_id, client),
            OctoprintRestartOctoprintButton(coordinator, device_id, client),
        ]
    )


class OctoprintPrinterButton(
    CoordinatorEntity[OctoprintDataUpdateCoordinator], ButtonEntity
):
    """Represent an OctoPrint binary sensor."""

    client: OctoprintClient

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        button_type: str,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator)
        self.client = client
        self._device_id = device_id
        self._attr_name = f"OctoPrint {button_type}"
        self._attr_unique_id = f"{button_type}-{device_id}"
        self._attr_device_info = coordinator.device_info

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


class OctoprintSystemButton(
    CoordinatorEntity[OctoprintDataUpdateCoordinator], ButtonEntity
):
    """Represent an OctoPrint binary sensor."""

    client: OctoprintClient

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        button_type: str,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator)
        self.client = client
        self._device_id = device_id
        self._attr_name = f"OctoPrint {button_type}"
        self._attr_unique_id = f"{button_type}-{device_id}"
        self._attr_device_info = coordinator.device_info

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


class OctoprintPauseJobButton(OctoprintPrinterButton):
    """Pause the active job."""

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator, "Pause Job", device_id, client)

    async def async_press(self) -> None:
        """Handle the button press."""
        printer: OctoprintPrinterInfo = self.coordinator.data["printer"]

        if printer.state.flags.printing:
            await self.client.pause_job()
        elif not printer.state.flags.paused and not printer.state.flags.pausing:
            raise InvalidPrinterState("Printer is not printing")


class OctoprintResumeJobButton(OctoprintPrinterButton):
    """Resume the active job."""

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator, "Resume Job", device_id, client)

    async def async_press(self) -> None:
        """Handle the button press."""
        printer: OctoprintPrinterInfo = self.coordinator.data["printer"]

        if printer.state.flags.paused:
            await self.client.resume_job()
        elif not printer.state.flags.printing and not printer.state.flags.resuming:
            raise InvalidPrinterState("Printer is not currently paused")


class OctoprintStopJobButton(OctoprintPrinterButton):
    """Resume the active job."""

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator, "Stop Job", device_id, client)

    async def async_press(self) -> None:
        """Handle the button press."""
        printer: OctoprintPrinterInfo = self.coordinator.data["printer"]

        if printer.state.flags.printing or printer.state.flags.paused:
            await self.client.cancel_job()


class OctoprintShutdownSystemButton(OctoprintSystemButton):
    """Shutdown the system."""

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator, "Shutdown System", device_id, client)

    async def async_press(self) -> None:
        """Handle the button press."""
        await self.client.shutdown()


class OctoprintRebootSystemButton(OctoprintSystemButton):
    """Reboot the system."""

    _attr_device_class = ButtonDeviceClass.RESTART

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator, "Reboot System", device_id, client)

    async def async_press(self) -> None:
        """Handle the button press."""
        await self.client.reboot_system()


class OctoprintRestartOctoprintButton(OctoprintSystemButton):
    """Restart Octoprint."""

    _attr_device_class = ButtonDeviceClass.RESTART

    def __init__(
        self,
        coordinator: OctoprintDataUpdateCoordinator,
        device_id: str,
        client: OctoprintClient,
    ) -> None:
        """Initialize a new OctoPrint button."""
        super().__init__(coordinator, "Restart Octoprint", device_id, client)

    async def async_press(self) -> None:
        """Handle the button press."""
        await self.client.restart()


class InvalidPrinterState(HomeAssistantError):
    """Service attempted in invalid state."""
