"""Support for Agent camera streaming."""
from datetime import timedelta
import logging

from agent import AgentError

from homeassistant.components.camera import SUPPORT_ON_OFF
from homeassistant.components.mjpeg.camera import (
    CONF_MJPEG_URL,
    CONF_STILL_IMAGE_URL,
    MjpegCamera,
    filter_urllib3_logging,
)
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME
from homeassistant.helpers import entity_platform

from .const import (
    ATTRIBUTION,
    CAMERA_SCAN_INTERVAL_SECS,
    CONNECTION,
    DOMAIN as AGENT_DOMAIN,
)

SCAN_INTERVAL = timedelta(seconds=CAMERA_SCAN_INTERVAL_SECS)

_LOGGER = logging.getLogger(__name__)

_DEV_EN_ALT = "enable_alerts"
_DEV_DS_ALT = "disable_alerts"
_DEV_EN_REC = "start_recording"
_DEV_DS_REC = "stop_recording"
_DEV_SNAP = "snapshot"

CAMERA_SERVICES = {
    _DEV_EN_ALT: "async_enable_alerts",
    _DEV_DS_ALT: "async_disable_alerts",
    _DEV_EN_REC: "async_start_recording",
    _DEV_DS_REC: "async_stop_recording",
    _DEV_SNAP: "async_snapshot",
}


async def async_setup_entry(
    hass, config_entry, async_add_entities, discovery_info=None
):
    """Set up the Agent cameras."""
    filter_urllib3_logging()
    cameras = []

    server = hass.data[AGENT_DOMAIN][config_entry.entry_id][CONNECTION]
    if not server.devices:
        _LOGGER.warning("Could not fetch cameras from Agent server")
        return

    for device in server.devices:
        if device.typeID == 2:
            camera = AgentCamera(device)
            cameras.append(camera)

    async_add_entities(cameras)

    platform = entity_platform.current_platform.get()
    for service, method in CAMERA_SERVICES.items():
        platform.async_register_entity_service(service, {}, method)


class AgentCamera(MjpegCamera):
    """Representation of an Agent Device Stream."""

    def __init__(self, device):
        """Initialize as a subclass of MjpegCamera."""
        self._servername = device.client.name
        self.server_url = device.client._server_url

        device_info = {
            CONF_NAME: device.name,
            CONF_MJPEG_URL: f"{self.server_url}{device.mjpeg_image_url}&size={device.mjpegStreamWidth}x{device.mjpegStreamHeight}",
            CONF_STILL_IMAGE_URL: f"{self.server_url}{device.still_image_url}&size={device.mjpegStreamWidth}x{device.mjpegStreamHeight}",
        }
        self.device = device
        self._removed = False
        self._name = f"{self._servername} {device.name}"
        self._unique_id = f"{device._client.unique}_{device.typeID}_{device.id}"
        super().__init__(device_info)

    @property
    def device_info(self):
        """Return the device info for adding the entity to the agent object."""
        return {
            "identifiers": {(AGENT_DOMAIN, self._unique_id)},
            "name": self._name,
            "manufacturer": "Agent",
            "model": "Camera",
            "sw_version": self.device.client.version,
        }

    async def async_update(self):
        """Update our state from the Agent API."""
        try:
            await self.device.update()
            if self._removed:
                _LOGGER.debug("%s reacquired", self._name)
            self._removed = False
        except AgentError:
            if self.device.client.is_available:  # server still available - camera error
                if not self._removed:
                    _LOGGER.error("%s lost", self._name)
                    self._removed = True

    @property
    def extra_state_attributes(self):
        """Return the Agent DVR camera state attributes."""
        return {
            ATTR_ATTRIBUTION: ATTRIBUTION,
            "editable": False,
            "enabled": self.is_on,
            "connected": self.connected,
            "detected": self.is_detected,
            "alerted": self.is_alerted,
            "has_ptz": self.device.has_ptz,
            "alerts_enabled": self.device.alerts_active,
        }

    @property
    def should_poll(self) -> bool:
        """Update the state periodically."""
        return True

    @property
    def is_recording(self) -> bool:
        """Return whether the monitor is recording."""
        return self.device.recording

    @property
    def is_alerted(self) -> bool:
        """Return whether the monitor has alerted."""
        return self.device.alerted

    @property
    def is_detected(self) -> bool:
        """Return whether the monitor has alerted."""
        return self.device.detected

    @property
    def available(self) -> bool:
        """Return True if entity is available."""
        return self.device.client.is_available

    @property
    def connected(self) -> bool:
        """Return True if entity is connected."""
        return self.device.connected

    @property
    def supported_features(self) -> int:
        """Return supported features."""
        return SUPPORT_ON_OFF

    @property
    def is_on(self) -> bool:
        """Return true if on."""
        return self.device.online

    @property
    def icon(self):
        """Return the icon to use in the frontend, if any."""
        if self.is_on:
            return "mdi:camcorder"
        return "mdi:camcorder-off"

    @property
    def motion_detection_enabled(self):
        """Return the camera motion detection status."""
        return self.device.detector_active

    @property
    def unique_id(self) -> str:
        """Return a unique identifier for this agent object."""
        return self._unique_id

    async def async_enable_alerts(self):
        """Enable alerts."""
        await self.device.alerts_on()

    async def async_disable_alerts(self):
        """Disable alerts."""
        await self.device.alerts_off()

    async def async_enable_motion_detection(self):
        """Enable motion detection."""
        await self.device.detector_on()

    async def async_disable_motion_detection(self):
        """Disable motion detection."""
        await self.device.detector_off()

    async def async_start_recording(self):
        """Start recording."""
        await self.device.record()

    async def async_stop_recording(self):
        """Stop recording."""
        await self.device.record_stop()

    async def async_turn_on(self):
        """Enable the camera."""
        await self.device.enable()

    async def async_snapshot(self):
        """Take a snapshot."""
        await self.device.snapshot()

    async def async_turn_off(self):
        """Disable the camera."""
        await self.device.disable()
