from typing import Any, Dict, Optional

import voluptuous as vol

from solax.inverter import Inverter
from solax.units import DailyTotal, Measurement, Total, Units
from solax.utils import (
    div10,
    div100,
    pack_u16,
    to_signed,
    to_signed32,
    twoway_div10,
    twoway_div100,
)


class X3Ultra(Inverter):
    """X3 Ultra v1.001.20"""

    # pylint: disable=duplicate-code
    _schema = vol.Schema(
        {
            vol.Required("type"): vol.All(int, 25),
            vol.Required("sn"): str,
            vol.Required("ver"): str,
            vol.Required("data"): vol.Schema(
                vol.All(
                    [vol.Coerce(float)],
                    vol.Length(min=300, max=300),
                )
            ),
            vol.Required("information"): vol.Schema(
                vol.All(vol.Length(min=10, max=10))
            ),
        },
        extra=vol.REMOVE_EXTRA,
    )

    @classmethod
    def build_all_variants(cls, host, port, pwd=""):
        return [cls._build(host, port, pwd, False)]

    @classmethod
    def _decode_run_mode(cls, run_mode):
        return {
            0: "Waiting",
            1: "Checking",
            2: "Normal",
            3: "Fault",
            4: "Permanent Fault",
            5: "Updating",
            6: "EPS Check",
            7: "EPS Mode",
            8: "Self Test",
            9: "Idle",
            10: "Standby",
        }.get(run_mode)

    @classmethod
    def response_decoder(cls):
        return {
            "Grid 1 Voltage": (0, Units.V, div10),
            "Grid 2 Voltage": (1, Units.V, div10),
            "Grid 3 Voltage": (2, Units.V, div10),
            "Grid 1 Current": (3, Units.A, twoway_div10),
            "Grid 2 Current": (4, Units.A, twoway_div10),
            "Grid 3 Current": (5, Units.A, twoway_div10),
            "Grid 1 Power": (6, Units.W, to_signed),
            "Grid 2 Power": (7, Units.W, to_signed),
            "Grid 3 Power": (8, Units.W, to_signed),
            "PV1 Voltage": (10, Units.V, div10),
            "PV2 Voltage": (11, Units.V, div10),
            "PV3 Voltage": (129, Units.V, div10),
            "PV1 Current": (12, Units.A, div10),
            "PV2 Current": (13, Units.A, div10),
            "PV3 Current": (130, Units.A, div10),
            "PV1 Power": (14, Units.W),
            "PV2 Power": (15, Units.W),
            "PV3 Power": (131, Units.W),
            "Grid 1 Frequency": (16, Units.HZ, div100),
            "Grid 2 Frequency": (17, Units.HZ, div100),
            "Grid 3 Frequency": (18, Units.HZ, div100),
            # "Run mode": (19, Units.NONE), # Only use the index once due to HA uids
            "Run mode text": (19, Units.NONE, X3Ultra._decode_run_mode),
            "EPS 1 Voltage": (23, Units.V, div10),
            "EPS 2 Voltage": (24, Units.V, div10),
            "EPS 3 Voltage": (25, Units.V, div10),
            "EPS 1 Current": (26, Units.A, twoway_div10),
            "EPS 2 Current": (27, Units.A, twoway_div10),
            "EPS 3 Current": (28, Units.A, twoway_div10),
            "EPS 1 Power": (29, Units.W, to_signed),
            "EPS 2 Power": (30, Units.W, to_signed),
            "EPS 3 Power": (31, Units.W, to_signed),
            "Grid Power ": (pack_u16(34, 35), Units.W, to_signed32),
            "Battery 1 Voltage": (39, Units.V, div10),
            "Battery 2 Voltage": (132, Units.V, div10),
            "Battery 1 Current": (40, Units.A, twoway_div100),
            "Battery 2 Current": (133, Units.A, twoway_div100),
            "Battery 1 Power": (41, Units.W, to_signed),
            "Battery 2 Power": (134, Units.W, to_signed),
            "Battery 1 Remaining Capacity": (103, Units.PERCENT),
            "Battery 2 Remaining Capacity": (140, Units.PERCENT),
            "Battery 1 Temperature": (105, Units.C, to_signed),
            "Battery 2 Temperature": (142, Units.C, to_signed),
            "Load/Generator Power": (47, Units.W, to_signed),
            "Radiator Temperature": (54, Units.C, to_signed),
            "Yield total": (pack_u16(58, 59), Total(Units.KWH), div10),
            "Yield today": (70, DailyTotal(Units.KWH), div10),
            "Battery Discharge Energy total": (
                pack_u16(74, 75),
                Total(Units.KWH),
                div10,
            ),
            "Battery Charge Energy total": (pack_u16(76, 77), Total(Units.KWH), div10),
            "Battery Discharge Energy today": (78, DailyTotal(Units.KWH), div10),
            "Battery Charge Energy today": (79, DailyTotal(Units.KWH), div10),
            "PV Energy total": (pack_u16(80, 81), Total(Units.KWH), div10),
            "EPS Energy total": (pack_u16(83, 84), Total(Units.KWH), div10),
            "EPS Energy today": (85, DailyTotal(Units.KWH), div10),
            "Feed-in Energy total": (pack_u16(86, 87), Total(Units.KWH), div100),
            "Grid Consumed Energy total": (pack_u16(88, 89), Total(Units.KWH), div100),
            "Feed-in Energy today": (pack_u16(90, 91), DailyTotal(Units.KWH), div100),
            "Grid Consumed Energy today": (
                pack_u16(92, 93),
                DailyTotal(Units.KWH),
                div100,
            ),
            "Battery Remaining Capacity": (158, Units.PERCENT),
            "Battery Remaining Energy": (
                106,
                Measurement(Units.KWH, storage=True),
                div10,
            ),
            "Inverter Power": (159, Units.W, div10),
        }

    # pylint: enable=duplicate-code

    @classmethod
    def inverter_serial_number_getter(cls, response: Dict[str, Any]) -> Optional[str]:
        return response["information"][2]
