from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.utility_meter import CONF_METER_TYPE, METER_TYPES
from homeassistant.const import UnitOfPower
from homeassistant.data_entry_flow import section
from homeassistant.helpers import selector
from homeassistant.helpers.selector import NumberSelector, NumberSelectorMode
import voluptuous as vol

from custom_components.powercalc.const import (
    CONF_APPLY_TO_ALL,
    CONF_COST_SENSOR_FRIENDLY_NAMING,
    CONF_COST_SENSOR_NAMING,
    CONF_CREATE_COST_SENSOR,
    CONF_CREATE_ENERGY_SENSOR,
    CONF_CREATE_UTILITY_METERS,
    CONF_ENERGY_FILTER_OUTLIER_ENABLED,
    CONF_ENERGY_FILTER_OUTLIER_MAX,
    CONF_ENERGY_INTEGRATION_METHOD,
    CONF_ENERGY_PRICE,
    CONF_ENERGY_PRICE_MULTIPLIER,
    CONF_ENERGY_PRICE_SENSOR,
    CONF_ENERGY_PRICE_SURCHARGE,
    CONF_ENERGY_SENSOR_UNIT_PREFIX,
    CONF_SUB_PROFILE,
    CONF_UTILITY_METER_NET_CONSUMPTION,
    CONF_UTILITY_METER_OFFSET,
    CONF_UTILITY_METER_TARIFFS,
    CONF_UTILITY_METER_TYPES,
    ENERGY_INTEGRATION_METHOD_LEFT,
    ENERGY_INTEGRATION_METHODS,
    UnitPrefix,
)
from custom_components.powercalc.power_profile.power_profile import PowerProfile

SCHEMA_UTILITY_METER_TOGGLE = vol.Schema(
    {
        vol.Optional(CONF_CREATE_UTILITY_METERS, default=False): selector.BooleanSelector(),
    },
)

SCHEMA_ENERGY_SENSOR_TOGGLE = vol.Schema(
    {
        vol.Optional(CONF_CREATE_ENERGY_SENSOR, default=True): selector.BooleanSelector(),
    },
)

SCHEMA_COST_SENSOR_TOGGLE = vol.Schema(
    {
        vol.Optional(CONF_CREATE_COST_SENSOR, default=False): selector.BooleanSelector(),
    },
)

SECTION_COST_PRICING = "cost_pricing"
SECTION_COST_NAMING = "cost_naming"
COST_DOCS_URI = "https://docs.powercalc.nl/sensor-types/cost-sensor/"

# The pricing fields are used both for the global energy price and for the per sensor
# price override, so they are defined once and reused by both schemas.
SCHEMA_COST_PRICING = vol.Schema(
    {
        vol.Optional(CONF_ENERGY_PRICE): NumberSelector(
            selector.NumberSelectorConfig(mode=NumberSelectorMode.BOX, step="any"),
        ),
        vol.Optional(CONF_ENERGY_PRICE_SENSOR): selector.EntitySelector(
            selector.EntitySelectorConfig(domain="sensor", device_class=SensorDeviceClass.MONETARY),
        ),
        vol.Optional(CONF_ENERGY_PRICE_SURCHARGE): NumberSelector(
            selector.NumberSelectorConfig(mode=NumberSelectorMode.BOX, step="any"),
        ),
        vol.Optional(CONF_ENERGY_PRICE_MULTIPLIER): NumberSelector(
            selector.NumberSelectorConfig(mode=NumberSelectorMode.BOX, step="any"),
        ),
    },
)

SCHEMA_GLOBAL_COST_NAMING = vol.Schema(
    {
        vol.Optional(CONF_COST_SENSOR_NAMING): selector.TextSelector(),
        vol.Optional(CONF_COST_SENSOR_FRIENDLY_NAMING): selector.TextSelector(),
    },
)

# Presented in the GUI as two collapsible sections (pricing and naming).
SCHEMA_GLOBAL_COST = vol.Schema(
    {
        vol.Required(SECTION_COST_PRICING): section(SCHEMA_COST_PRICING),
        vol.Required(SECTION_COST_NAMING): section(SCHEMA_GLOBAL_COST_NAMING, {"collapsed": True}),
    },
)

# Flat variant with all cost keys, used to merge/clear the (un)nested user input.
SCHEMA_GLOBAL_COST_FLAT = SCHEMA_COST_PRICING.extend(SCHEMA_GLOBAL_COST_NAMING.schema)

# Shown when the global create_cost_sensors toggle is flipped, to optionally propagate the
# change to all existing GUI sensors.
SCHEMA_COST_APPLY = vol.Schema(
    {
        vol.Optional(CONF_APPLY_TO_ALL, default=True): selector.BooleanSelector(),
    },
)

SCHEMA_ENERGY_OPTIONS = vol.Schema(
    {
        vol.Optional(
            CONF_ENERGY_INTEGRATION_METHOD,
            default=ENERGY_INTEGRATION_METHOD_LEFT,
        ): selector.SelectSelector(
            selector.SelectSelectorConfig(
                options=ENERGY_INTEGRATION_METHODS,
                mode=selector.SelectSelectorMode.DROPDOWN,
            ),
        ),
        vol.Optional(CONF_ENERGY_SENSOR_UNIT_PREFIX): selector.SelectSelector(
            selector.SelectSelectorConfig(
                options=[
                    selector.SelectOptionDict(value=UnitPrefix.KILO, label="k (kilo)"),
                    selector.SelectOptionDict(value=UnitPrefix.MEGA, label="M (mega)"),
                    selector.SelectOptionDict(value=UnitPrefix.GIGA, label="G (giga)"),
                    selector.SelectOptionDict(value=UnitPrefix.TERA, label="T (tera)"),
                ],
                mode=selector.SelectSelectorMode.DROPDOWN,
            ),
        ),
    },
)

SCHEMA_SENSOR_ENERGY_OPTIONS = SCHEMA_ENERGY_OPTIONS.extend(
    vol.Schema(
        {
            vol.Optional(CONF_ENERGY_FILTER_OUTLIER_ENABLED, default=False): selector.BooleanSelector(),
            vol.Optional(CONF_ENERGY_FILTER_OUTLIER_MAX): NumberSelector(
                selector.NumberSelectorConfig(mode=NumberSelectorMode.BOX, unit_of_measurement=UnitOfPower.WATT),
            ),
        },
    ).schema,
)


SCHEMA_UTILITY_METER_OPTIONS = vol.Schema(
    {
        vol.Required(CONF_UTILITY_METER_TYPES): selector.SelectSelector(
            selector.SelectSelectorConfig(
                options=METER_TYPES,
                translation_key=CONF_METER_TYPE,
                multiple=True,
            ),
        ),
        vol.Optional(CONF_UTILITY_METER_TARIFFS, default=[]): selector.SelectSelector(
            selector.SelectSelectorConfig(options=[], custom_value=True, multiple=True),
        ),
        vol.Optional(CONF_UTILITY_METER_NET_CONSUMPTION, default=False): selector.BooleanSelector(),
        vol.Required(CONF_UTILITY_METER_OFFSET, default=0): selector.NumberSelector(
            selector.NumberSelectorConfig(
                min=0,
                max=28,
                mode=selector.NumberSelectorMode.BOX,
                unit_of_measurement="days",
            ),
        ),
    },
)


async def build_sub_profile_schema(
    profile: PowerProfile,
    selected_sub_profile: str | None,
) -> vol.Schema:
    """Create sub profile schema."""
    sub_profiles = [
        selector.SelectOptionDict(
            value=sub_profile[0],
            label=sub_profile[1]["name"] if "name" in sub_profile[1] else sub_profile[0],
        )
        for sub_profile in await profile.get_sub_profiles()
    ]
    return vol.Schema(
        {
            vol.Required(
                CONF_SUB_PROFILE,
                description={"suggested_value": selected_sub_profile},
                default=selected_sub_profile,
            ): selector.SelectSelector(
                selector.SelectSelectorConfig(
                    options=sub_profiles,
                    mode=selector.SelectSelectorMode.DROPDOWN,
                ),
            ),
        },
    )
