"""Read status of growatt inverters."""
import re
import json
import logging
import datetime

import growattServer
import voluptuous as vol

from homeassistant.util import Throttle
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_NAME, CONF_USERNAME, CONF_PASSWORD

_LOGGER = logging.getLogger(__name__)

CONF_PLANT_ID = "plant_id"
DEFAULT_PLANT_ID = "0"
DEFAULT_NAME = "Growatt"
SCAN_INTERVAL = datetime.timedelta(minutes=5)

TOTAL_SENSOR_TYPES = {
    "total_money_today": ("Total money today", "€", "plantMoneyText", None),
    "total_money_total": ("Money lifetime", "€", "totalMoneyText", None),
    "total_energy_today": ("Energy Today", "kWh", "todayEnergy", "power"),
    "total_output_power": ("Output Power", "W", "invTodayPpv", "power"),
    "total_energy_output": ("Lifetime energy output", "kWh", "totalEnergy", "power"),
    "total_maximum_output": ("Maximum power", "W", "nominalPower", "power"),
}

INVERTER_SENSOR_TYPES = {
    "inverter_energy_today": ("Energy today", "kWh", "e_today", "power"),
    "inverter_energy_total": ("Lifetime energy output", "kWh", "e_total", "power"),
    "inverter_voltage_input_1": ("Input 1 voltage", "V", "vpv1", None),
    "inverter_amperage_input_1": ("Input 1 Amperage", "A", "ipv1", None),
    "inverter_wattage_input_1": ("Input 1 Wattage", "W", "ppv1", "power"),
    "inverter_voltage_input_2": ("Input 2 voltage", "V", "vpv2", None),
    "inverter_amperage_input_2": ("Input 2 Amperage", "A", "ipv2", None),
    "inverter_wattage_input_2": ("Input 2 Wattage", "W", "ppv2", "power"),
    "inverter_voltage_input_3": ("Input 3 voltage", "V", "vpv3", None),
    "inverter_amperage_input_3": ("Input 3 Amperage", "A", "ipv3", None),
    "inverter_wattage_input_3": ("Input 3 Wattage", "W", "ppv3", "power"),
    "inverter_internal_wattage": ("Internal wattage", "W", "ppv", "power"),
    "inverter_reactive_voltage": ("Reactive voltage", "V", "vacr", None),
    "inverter_inverter_reactive_amperage": ("Reactive amperage", "A", "iacr", None),
    "inverter_frequency": ("AC frequency", "Hz", "fac", None),
    "inverter_current_wattage": ("Output power", "W", "pac", "power"),
    "inverter_current_reactive_wattage": ("Reactive wattage", "W", "pacr", "power"),
}

SENSOR_TYPES = {**TOTAL_SENSOR_TYPES, **INVERTER_SENSOR_TYPES}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
        vol.Optional(CONF_PLANT_ID, default=DEFAULT_PLANT_ID): cv.string,
        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 Growatt sensor."""
    username = config[CONF_USERNAME]
    password = config[CONF_PASSWORD]
    plant_id = config[CONF_PLANT_ID]
    name = config[CONF_NAME]

    api = growattServer.GrowattApi()

    # Log in to api and fetch first plant if no plant id is defined.
    login_response = api.login(username, password)
    if not login_response["success"] and login_response["errCode"] == "102":
        _LOGGER.error("Username or Password may be incorrect!")
        return
    user_id = login_response["userId"]
    if plant_id == DEFAULT_PLANT_ID:
        plant_info = api.plant_list(user_id)
        plant_id = plant_info["data"][0]["plantId"]

    # Get a list of inverters for specified plant to add sensors for.
    inverters = api.inverter_list(plant_id)
    entities = []
    probe = GrowattData(api, username, password, plant_id, True)
    for sensor in TOTAL_SENSOR_TYPES:
        entities.append(
            GrowattInverter(probe, f"{name} Total", sensor, f"{plant_id}-{sensor}")
        )

    # Add sensors for each inverter in the specified plant.
    for inverter in inverters:
        probe = GrowattData(api, username, password, inverter["deviceSn"], False)
        for sensor in INVERTER_SENSOR_TYPES:
            entities.append(
                GrowattInverter(
                    probe,
                    f"{inverter['deviceAilas']}",
                    sensor,
                    f"{inverter['deviceSn']}-{sensor}",
                )
            )

    add_entities(entities, True)


class GrowattInverter(Entity):
    """Representation of a Growatt Sensor."""

    def __init__(self, probe, name, sensor, unique_id):
        """Initialize a PVOutput sensor."""
        self.sensor = sensor
        self.probe = probe
        self._name = name
        self._state = None
        self._unique_id = unique_id

    @property
    def name(self):
        """Return the name of the sensor."""
        return f"{self._name} {SENSOR_TYPES[self.sensor][0]}"

    @property
    def unique_id(self):
        """Return the unique id of the sensor."""
        return self._unique_id

    @property
    def icon(self):
        """Return the icon of the sensor."""
        return "mdi:solar-power"

    @property
    def state(self):
        """Return the state of the sensor."""
        return self.probe.get_data(SENSOR_TYPES[self.sensor][2])

    @property
    def device_class(self):
        """Return the device class of the sensor."""
        return SENSOR_TYPES[self.sensor][3]

    @property
    def unit_of_measurement(self):
        """Return the unit of measurement of this entity, if any."""
        return SENSOR_TYPES[self.sensor][1]

    def update(self):
        """Get the latest data from the Growat API and updates the state."""
        self.probe.update()


class GrowattData:
    """The class for handling data retrieval."""

    def __init__(self, api, username, password, inverter_id, is_total=False):
        """Initialize the probe."""

        self.is_total = is_total
        self.api = api
        self.inverter_id = inverter_id
        self.data = {}
        self.username = username
        self.password = password

    @Throttle(SCAN_INTERVAL)
    def update(self):
        """Update probe data."""
        self.api.login(self.username, self.password)
        _LOGGER.debug("Updating data for %s", self.inverter_id)
        try:
            if self.is_total:
                total_info = self.api.plant_info(self.inverter_id)
                del total_info["deviceList"]
                # PlantMoneyText comes in as "3.1/€" remove anything that isn't part of the number
                total_info["plantMoneyText"] = re.sub(
                    r"[^\d.,]", "", total_info["plantMoneyText"]
                )
                self.data = total_info
            else:
                inverter_info = self.api.inverter_detail(self.inverter_id)
                self.data = inverter_info["data"]
        except json.decoder.JSONDecodeError:
            _LOGGER.error("Unable to fetch data from Growatt server")

    def get_data(self, variable):
        """Get the data."""
        return self.data.get(variable)
