"""Support for Rheem EcoNet water heaters."""
import datetime
import logging

from pyeconet.api import PyEcoNet
import voluptuous as vol

from homeassistant.components.water_heater import (
    PLATFORM_SCHEMA,
    STATE_ECO,
    STATE_ELECTRIC,
    STATE_GAS,
    STATE_HEAT_PUMP,
    STATE_HIGH_DEMAND,
    STATE_OFF,
    STATE_PERFORMANCE,
    SUPPORT_OPERATION_MODE,
    SUPPORT_TARGET_TEMPERATURE,
    WaterHeaterEntity,
)
from homeassistant.const import (
    ATTR_ENTITY_ID,
    ATTR_TEMPERATURE,
    CONF_PASSWORD,
    CONF_USERNAME,
    TEMP_FAHRENHEIT,
)
import homeassistant.helpers.config_validation as cv

from .const import DOMAIN, SERVICE_ADD_VACATION, SERVICE_DELETE_VACATION

_LOGGER = logging.getLogger(__name__)

ATTR_VACATION_START = "next_vacation_start_date"
ATTR_VACATION_END = "next_vacation_end_date"
ATTR_ON_VACATION = "on_vacation"
ATTR_TODAYS_ENERGY_USAGE = "todays_energy_usage"
ATTR_IN_USE = "in_use"

ATTR_START_DATE = "start_date"
ATTR_END_DATE = "end_date"

ATTR_LOWER_TEMP = "lower_temp"
ATTR_UPPER_TEMP = "upper_temp"
ATTR_IS_ENABLED = "is_enabled"

SUPPORT_FLAGS_HEATER = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE

ADD_VACATION_SCHEMA = vol.Schema(
    {
        vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
        vol.Optional(ATTR_START_DATE): cv.positive_int,
        vol.Required(ATTR_END_DATE): cv.positive_int,
    }
)

DELETE_VACATION_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids})

ECONET_DATA = "econet"

ECONET_STATE_TO_HA = {
    "Energy Saver": STATE_ECO,
    "gas": STATE_GAS,
    "High Demand": STATE_HIGH_DEMAND,
    "Off": STATE_OFF,
    "Performance": STATE_PERFORMANCE,
    "Heat Pump Only": STATE_HEAT_PUMP,
    "Electric-Only": STATE_ELECTRIC,
    "Electric": STATE_ELECTRIC,
    "Heat Pump": STATE_HEAT_PUMP,
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)


def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the EcoNet water heaters."""

    hass.data[ECONET_DATA] = {}
    hass.data[ECONET_DATA]["water_heaters"] = []

    username = config.get(CONF_USERNAME)
    password = config.get(CONF_PASSWORD)

    econet = PyEcoNet(username, password)
    water_heaters = econet.get_water_heaters()
    hass_water_heaters = [
        EcoNetWaterHeater(water_heater) for water_heater in water_heaters
    ]
    add_entities(hass_water_heaters)
    hass.data[ECONET_DATA]["water_heaters"].extend(hass_water_heaters)

    def service_handle(service):
        """Handle the service calls."""
        entity_ids = service.data.get("entity_id")
        all_heaters = hass.data[ECONET_DATA]["water_heaters"]
        _heaters = [
            x for x in all_heaters if not entity_ids or x.entity_id in entity_ids
        ]

        for _water_heater in _heaters:
            if service.service == SERVICE_ADD_VACATION:
                start = service.data.get(ATTR_START_DATE)
                end = service.data.get(ATTR_END_DATE)
                _water_heater.add_vacation(start, end)
            if service.service == SERVICE_DELETE_VACATION:
                for vacation in _water_heater.water_heater.vacations:
                    vacation.delete()

            _water_heater.schedule_update_ha_state(True)

    hass.services.register(
        DOMAIN, SERVICE_ADD_VACATION, service_handle, schema=ADD_VACATION_SCHEMA
    )

    hass.services.register(
        DOMAIN, SERVICE_DELETE_VACATION, service_handle, schema=DELETE_VACATION_SCHEMA
    )


class EcoNetWaterHeater(WaterHeaterEntity):
    """Representation of an EcoNet water heater."""

    def __init__(self, water_heater):
        """Initialize the water heater."""
        self.water_heater = water_heater
        self.supported_modes = self.water_heater.supported_modes
        self.econet_state_to_ha = {}
        self.ha_state_to_econet = {}
        for mode in ECONET_STATE_TO_HA:
            if mode in self.supported_modes:
                self.econet_state_to_ha[mode] = ECONET_STATE_TO_HA.get(mode)
        for key, value in self.econet_state_to_ha.items():
            self.ha_state_to_econet[value] = key
        for mode in self.supported_modes:
            if mode not in ECONET_STATE_TO_HA:
                error = f"Invalid operation mode mapping. {mode} doesn't map. Please report this."
                _LOGGER.error(error)

    @property
    def name(self):
        """Return the device name."""
        return self.water_heater.name

    @property
    def available(self):
        """Return if the the device is online or not."""
        return self.water_heater.is_connected

    @property
    def temperature_unit(self):
        """Return the unit of measurement."""
        return TEMP_FAHRENHEIT

    @property
    def device_state_attributes(self):
        """Return the optional device state attributes."""
        data = {}
        vacations = self.water_heater.get_vacations()
        if vacations:
            data[ATTR_VACATION_START] = vacations[0].start_date
            data[ATTR_VACATION_END] = vacations[0].end_date
        data[ATTR_ON_VACATION] = self.water_heater.is_on_vacation
        todays_usage = self.water_heater.total_usage_for_today
        if todays_usage:
            data[ATTR_TODAYS_ENERGY_USAGE] = todays_usage
        data[ATTR_IN_USE] = self.water_heater.in_use

        if self.water_heater.lower_temp is not None:
            data[ATTR_LOWER_TEMP] = round(self.water_heater.lower_temp, 2)
        if self.water_heater.upper_temp is not None:
            data[ATTR_UPPER_TEMP] = round(self.water_heater.upper_temp, 2)
        if self.water_heater.is_enabled is not None:
            data[ATTR_IS_ENABLED] = self.water_heater.is_enabled

        return data

    @property
    def current_operation(self):
        """
        Return current operation as one of the following.

        ["eco", "heat_pump", "high_demand", "electric_only"]
        """
        current_op = self.econet_state_to_ha.get(self.water_heater.mode)
        return current_op

    @property
    def operation_list(self):
        """List of available operation modes."""
        op_list = []
        for mode in self.supported_modes:
            ha_mode = self.econet_state_to_ha.get(mode)
            if ha_mode is not None:
                op_list.append(ha_mode)
        return op_list

    @property
    def supported_features(self):
        """Return the list of supported features."""
        return SUPPORT_FLAGS_HEATER

    def set_temperature(self, **kwargs):
        """Set new target temperature."""
        target_temp = kwargs.get(ATTR_TEMPERATURE)
        if target_temp is not None:
            self.water_heater.set_target_set_point(target_temp)
        else:
            _LOGGER.error("A target temperature must be provided")

    def set_operation_mode(self, operation_mode):
        """Set operation mode."""
        op_mode_to_set = self.ha_state_to_econet.get(operation_mode)
        if op_mode_to_set is not None:
            self.water_heater.set_mode(op_mode_to_set)
        else:
            _LOGGER.error("An operation mode must be provided")

    def add_vacation(self, start, end):
        """Add a vacation to this water heater."""
        if not start:
            start = datetime.datetime.now()
        else:
            start = datetime.datetime.fromtimestamp(start)
        end = datetime.datetime.fromtimestamp(end)
        self.water_heater.set_vacation_mode(start, end)

    def update(self):
        """Get the latest date."""
        self.water_heater.update_state()

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self.water_heater.set_point

    @property
    def min_temp(self):
        """Return the minimum temperature."""
        return self.water_heater.min_set_point

    @property
    def max_temp(self):
        """Return the maximum temperature."""
        return self.water_heater.max_set_point
