"""Define /location endpoints."""
from typing import Awaitable, Callable, Optional

from .const import API_V2_BASE
from .util import raise_on_invalid_argument

SYSTEM_MODE_AWAY = "away"
SYSTEM_MODE_HOME = "home"
SYSTEM_MODE_SLEEP = "sleep"
SYSTEM_MODES = set([SYSTEM_MODE_AWAY, SYSTEM_MODE_HOME, SYSTEM_MODE_SLEEP])
SYSTEM_REVERT_MODES = set([SYSTEM_MODE_AWAY, SYSTEM_MODE_HOME])

# The Flo web app hardcodes sleep durations to certain intervals; testing seems to
# indicate that those are the only valid values, so we stick with them:
SLEEP_MINUTE_OPTIONS = set([120, 1440, 4320])


class Location:
    """Define an object to handle the endpoints."""

    def __init__(self, request: Callable[..., Awaitable]) -> None:
        """Initialize."""
        self._request: Callable[..., Awaitable] = request

    async def _set_system_mode(
        self, location_id: str, mode: str, additional_payload: Optional[dict] = None
    ) -> None:
        """Set the system mode (with optional parameters)."""
        raise_on_invalid_argument(mode, SYSTEM_MODES)

        payload = {"target": mode}
        if additional_payload:
            payload = {**payload, **additional_payload}
        await self._request(
            "post", f"{API_V2_BASE}/locations/{location_id}/systemMode", json=payload
        )

    async def get_info(
        self, location_id: str, include_device_info: bool = False,
    ) -> dict:
        """Return user account data.

        :param location_id: A Flo location UUID
        :type location_id: ``str``
        :param include_device_info: Include expanded device information
        :type include_device_info: ``bool``
        :rtype: ``dict``
        """
        additional_info = []
        if include_device_info:
            additional_info.append("devices")

        params = {}
        if additional_info:
            params["expand"] = ",".join(additional_info)

        return await self._request(
            "get", f"{API_V2_BASE}/locations/{location_id}", params=params
        )

    async def set_mode_away(self, location_id: str) -> None:
        """Set the system mode to "Away".

        :param location_id: A Flo location UUID
        :type location_id: ``str``
        """
        await self._set_system_mode(location_id, SYSTEM_MODE_AWAY)

    async def set_mode_home(self, location_id: str) -> None:
        """Set the system mode to "Home".

        :param location_id: A Flo location UUID
        :type location_id: ``str``
        """
        await self._set_system_mode(location_id, SYSTEM_MODE_HOME)

    async def set_mode_sleep(
        self,
        location_id: str,
        revert_minutes: int,
        revert_mode: Optional[str] = SYSTEM_MODE_HOME,
    ) -> None:
        """Set the system mode to "Home".

        :param location_id: A Flo location UUID
        :type location_id: ``str``
        :param revert_minutes: The number of minutes to sleep (120, 1440, or 4320)
        :type revert_minutes: ``int``
        :param revert_mode: The mode to set after sleep concludes ("away" or "home")
        :type revert_mode: ``str``
        """
        raise_on_invalid_argument(revert_minutes, SLEEP_MINUTE_OPTIONS)
        raise_on_invalid_argument(revert_mode, SYSTEM_REVERT_MODES)

        await self._set_system_mode(
            location_id,
            SYSTEM_MODE_SLEEP,
            additional_payload={
                "revertMinutes": revert_minutes,
                "revertMode": revert_mode,
            },
        )
