"""Camera that loads a picture from a local file."""

from __future__ import annotations

import logging
import os

import voluptuous as vol
from homeassistant.components.camera import Camera
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST
from homeassistant.core import ServiceCall

from .const import (
    ATTR_AMAZON_IMAGE,
    ATTR_IMAGE_NAME,
    ATTR_IMAGE_PATH,
    CAMERA,
    CAMERA_DATA,
    CONF_CUSTOM_IMG,
    CONF_CUSTOM_IMG_FILE,
    COORDINATOR,
    DOMAIN,
    SENSOR_NAME,
    VERSION,
)

SERVICE_UPDATE_IMAGE = "update_image"
_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass, config, async_add_entities):
    """Set up the Camera that works with local files."""
    if CAMERA not in hass.data[DOMAIN][config.entry_id]:
        hass.data[DOMAIN][config.entry_id][CAMERA] = []

    coordinator = hass.data[DOMAIN][config.entry_id][COORDINATOR]
    camera = []
    if not config.data.get(CONF_CUSTOM_IMG):
        file_path = f"{os.path.dirname(__file__)}/mail_none.gif"
    else:
        file_path = config.data.get(CONF_CUSTOM_IMG_FILE)

    for variable in CAMERA_DATA:
        temp_cam = MailCam(hass, variable, config, coordinator, file_path)
        camera.append(temp_cam)
        hass.data[DOMAIN][config.entry_id][CAMERA].append(temp_cam)

    async def _update_image(service: ServiceCall) -> None:
        """Refresh camera image."""
        _LOGGER.debug("Updating image: %s", service)
        cameras = hass.data[DOMAIN][config.entry_id][CAMERA]
        entity_id = None

        if ATTR_ENTITY_ID in service.data.keys():
            entity_id = service.data[ATTR_ENTITY_ID]

        # Update all cameras if no entity_id
        if entity_id is None:
            for cam in cameras:
                cam.update_file_path()

        else:
            for cam in cameras:
                if cam.entity_id in entity_id:
                    cam.update_file_path()
        return True

    hass.services.async_register(
        DOMAIN,
        SERVICE_UPDATE_IMAGE,
        _update_image,
        schema=vol.Schema(
            {
                vol.Optional(ATTR_ENTITY_ID): vol.Coerce(str),
            }
        ),
    )

    async_add_entities(camera)


class MailCam(Camera):
    """Representation of a local file camera."""

    def __init__(
        self,
        hass,
        name: str,
        config: ConfigEntry,
        coordinator,
        file_path: str,
    ) -> None:
        """Initialize Local File Camera component."""
        super().__init__()

        self.hass = hass
        self._name = CAMERA_DATA[name][SENSOR_NAME]
        self._type = name
        self.check_file_path_access(file_path)
        self._file_path = file_path
        self._coordinator = coordinator
        self._host = config.data.get(CONF_HOST)
        self._unique_id = config.entry_id
        self._no_mail = (
            None
            if not config.data.get(CONF_CUSTOM_IMG)
            else config.data.get(CONF_CUSTOM_IMG_FILE)
        )

    async def async_camera_image(
        self, width: int | None = None, height: int | None = None
    ) -> bytes | None:
        """Return image response."""
        try:
            file = await self.hass.async_add_executor_job(open, self._file_path, "rb")
            return file.read()
        except FileNotFoundError:
            _LOGGER.info(
                "Could not read camera %s image from file: %s",
                self._name,
                self._file_path,
            )

    def check_file_path_access(self, file_path: str) -> None:
        """Check that filepath given is readable."""
        if not os.access(file_path, os.R_OK):
            _LOGGER.info(
                "Could not read camera %s image from file: %s", self._name, file_path
            )

    def update_file_path(self) -> None:
        """Update the file_path."""
        _LOGGER.debug("Camera Update: %s", self._type)
        _LOGGER.debug("Custom No Mail: %s", self._no_mail)
        file_path = None

        if not self._coordinator.last_update_success:
            _LOGGER.debug("Update to update camera image. Unavailable.")
            return

        if self._coordinator.data is None:
            _LOGGER.debug("Unable to update camera image, no data.")
            return

        if self._type == "usps_camera":
            # Update camera image for USPS informed delivery images
            file_path = f"{os.path.dirname(__file__)}/mail_none.gif"
            s1 = set([ATTR_IMAGE_NAME, ATTR_IMAGE_PATH])
            if s1.issubset(self._coordinator.data.keys()):
                image = self._coordinator.data[ATTR_IMAGE_NAME]
                path = self._coordinator.data[ATTR_IMAGE_PATH]
                file_path = f"{self.hass.config.path()}/{path}{image}"
            else:
                if self._no_mail:
                    file_path = self._no_mail

        elif self._type == "amazon_camera":
            # Update camera image for Amazon deliveries
            file_path = f"{os.path.dirname(__file__)}/no_deliveries.jpg"
            s1 = set([ATTR_AMAZON_IMAGE, ATTR_IMAGE_PATH])
            if s1.issubset(self._coordinator.data.keys()):
                image = self._coordinator.data[ATTR_AMAZON_IMAGE]
                path = f"{self._coordinator.data[ATTR_IMAGE_PATH]}amazon/"
                file_path = f"{self.hass.config.path()}/{path}{image}"

        self.check_file_path_access(file_path)
        self._file_path = file_path
        self.schedule_update_ha_state()

    async def async_on_demand_update(self):
        """Update state."""
        self.async_schedule_update_ha_state(True)

    @property
    def device_info(self) -> dict:
        """Return device information about the mailbox."""
        return {
            "connections": {(DOMAIN, self._unique_id)},
            "name": self._host,
            "manufacturer": "IMAP E-Mail",
            "sw_version": VERSION,
        }

    @property
    def unique_id(self) -> str:
        """Return a unique, Home Assistant friendly identifier for this entity."""
        return f"{self._host}_{self._name}_{self._unique_id}"

    @property
    def name(self):
        """Return the name of this camera."""
        return self._name

    @property
    def extra_state_attributes(self):
        """Return the camera state attributes."""
        return {"file_path": self._file_path}

    @property
    def should_poll(self) -> bool:
        """Return True if entity has to be polled for state.

        False if entity pushes its state to HA.
        """
        return True

    async def async_update(self):
        """Update camera entity and refresh attributes."""
        self.update_file_path()

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