"""Schema definitions for tibber_prices config flow."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from collections.abc import Mapping

import voluptuous as vol

from custom_components.tibber_prices.const import (
    BEST_PRICE_MAX_LEVEL_OPTIONS,
    CONF_AVERAGE_SENSOR_DISPLAY,
    CONF_BEST_PRICE_EXTEND_TO_VERY_CHEAP,
    CONF_BEST_PRICE_FLEX,
    CONF_BEST_PRICE_GEOMETRIC_FLEX,
    CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS,
    CONF_BEST_PRICE_MAX_LEVEL,
    CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
    CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
    CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
    CONF_BEST_PRICE_SEGMENT_FORCING,
    CONF_BEST_PRICE_SEGMENT_MIN_PERIODS,
    CONF_CURRENCY_DISPLAY_MODE,
    CONF_ENABLE_MIN_PERIODS_BEST,
    CONF_ENABLE_MIN_PERIODS_PEAK,
    CONF_EXTENDED_DESCRIPTIONS,
    CONF_MIN_PERIODS_BEST,
    CONF_MIN_PERIODS_PEAK,
    CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE,
    CONF_PEAK_PRICE_FLEX,
    CONF_PEAK_PRICE_GEOMETRIC_FLEX,
    CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS,
    CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
    CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
    CONF_PEAK_PRICE_MIN_LEVEL,
    CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
    CONF_PEAK_PRICE_SEGMENT_FORCING,
    CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS,
    CONF_PRICE_LEVEL_GAP_TOLERANCE,
    CONF_PRICE_RATING_GAP_TOLERANCE,
    CONF_PRICE_RATING_HYSTERESIS,
    CONF_PRICE_RATING_THRESHOLD_HIGH,
    CONF_PRICE_RATING_THRESHOLD_LOW,
    CONF_PRICE_TREND_CHANGE_CONFIRMATION,
    CONF_PRICE_TREND_MIN_PRICE_CHANGE,
    CONF_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
    CONF_PRICE_TREND_THRESHOLD_FALLING,
    CONF_PRICE_TREND_THRESHOLD_RISING,
    CONF_PRICE_TREND_THRESHOLD_STRONGLY_FALLING,
    CONF_PRICE_TREND_THRESHOLD_STRONGLY_RISING,
    CONF_RELAXATION_ATTEMPTS_BEST,
    CONF_RELAXATION_ATTEMPTS_PEAK,
    CONF_VIRTUAL_TIME_OFFSET_DAYS,
    CONF_VIRTUAL_TIME_OFFSET_HOURS,
    CONF_VIRTUAL_TIME_OFFSET_MINUTES,
    CONF_VOLATILITY_THRESHOLD_HIGH,
    CONF_VOLATILITY_THRESHOLD_MODERATE,
    CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
    DEFAULT_AVERAGE_SENSOR_DISPLAY,
    DEFAULT_BEST_PRICE_EXTEND_TO_VERY_CHEAP,
    DEFAULT_BEST_PRICE_FLEX,
    DEFAULT_BEST_PRICE_GEOMETRIC_FLEX,
    DEFAULT_BEST_PRICE_MAX_EXTENSION_INTERVALS,
    DEFAULT_BEST_PRICE_MAX_LEVEL,
    DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
    DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
    DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
    DEFAULT_BEST_PRICE_SEGMENT_FORCING,
    DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS,
    DEFAULT_ENABLE_MIN_PERIODS_BEST,
    DEFAULT_ENABLE_MIN_PERIODS_PEAK,
    DEFAULT_EXTENDED_DESCRIPTIONS,
    DEFAULT_MIN_PERIODS_BEST,
    DEFAULT_MIN_PERIODS_PEAK,
    DEFAULT_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE,
    DEFAULT_PEAK_PRICE_FLEX,
    DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX,
    DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS,
    DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
    DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
    DEFAULT_PEAK_PRICE_MIN_LEVEL,
    DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
    DEFAULT_PEAK_PRICE_SEGMENT_FORCING,
    DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS,
    DEFAULT_PRICE_LEVEL_GAP_TOLERANCE,
    DEFAULT_PRICE_RATING_GAP_TOLERANCE,
    DEFAULT_PRICE_RATING_HYSTERESIS,
    DEFAULT_PRICE_RATING_THRESHOLD_HIGH,
    DEFAULT_PRICE_RATING_THRESHOLD_LOW,
    DEFAULT_PRICE_TREND_CHANGE_CONFIRMATION,
    DEFAULT_PRICE_TREND_MIN_PRICE_CHANGE,
    DEFAULT_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
    DEFAULT_PRICE_TREND_THRESHOLD_FALLING,
    DEFAULT_PRICE_TREND_THRESHOLD_RISING,
    DEFAULT_PRICE_TREND_THRESHOLD_STRONGLY_FALLING,
    DEFAULT_PRICE_TREND_THRESHOLD_STRONGLY_RISING,
    DEFAULT_RELAXATION_ATTEMPTS_BEST,
    DEFAULT_RELAXATION_ATTEMPTS_PEAK,
    DEFAULT_VIRTUAL_TIME_OFFSET_DAYS,
    DEFAULT_VIRTUAL_TIME_OFFSET_HOURS,
    DEFAULT_VIRTUAL_TIME_OFFSET_MINUTES,
    DEFAULT_VOLATILITY_THRESHOLD_HIGH,
    DEFAULT_VOLATILITY_THRESHOLD_MODERATE,
    DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
    DISPLAY_MODE_BASE,
    DISPLAY_MODE_SUBUNIT,
    MAX_EXTENSION_INTERVALS,
    MAX_GAP_COUNT,
    MAX_GEOMETRIC_FLEX,
    MAX_MIN_PERIOD_LENGTH,
    MAX_MIN_PERIODS,
    MAX_PRICE_LEVEL_GAP_TOLERANCE,
    MAX_PRICE_RATING_GAP_TOLERANCE,
    MAX_PRICE_RATING_HYSTERESIS,
    MAX_PRICE_RATING_THRESHOLD_HIGH,
    MAX_PRICE_RATING_THRESHOLD_LOW,
    MAX_PRICE_TREND_CHANGE_CONFIRMATION,
    MAX_PRICE_TREND_FALLING,
    MAX_PRICE_TREND_MIN_PRICE_CHANGE,
    MAX_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
    MAX_PRICE_TREND_RISING,
    MAX_PRICE_TREND_STRONGLY_FALLING,
    MAX_PRICE_TREND_STRONGLY_RISING,
    MAX_RELAXATION_ATTEMPTS,
    MAX_SEGMENT_MIN_PERIODS,
    MAX_VOLATILITY_THRESHOLD_HIGH,
    MAX_VOLATILITY_THRESHOLD_MODERATE,
    MAX_VOLATILITY_THRESHOLD_VERY_HIGH,
    MIN_GAP_COUNT,
    MIN_PERIOD_LENGTH,
    MIN_PRICE_LEVEL_GAP_TOLERANCE,
    MIN_PRICE_RATING_GAP_TOLERANCE,
    MIN_PRICE_RATING_HYSTERESIS,
    MIN_PRICE_RATING_THRESHOLD_HIGH,
    MIN_PRICE_RATING_THRESHOLD_LOW,
    MIN_PRICE_TREND_CHANGE_CONFIRMATION,
    MIN_PRICE_TREND_FALLING,
    MIN_PRICE_TREND_MIN_PRICE_CHANGE,
    MIN_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
    MIN_PRICE_TREND_RISING,
    MIN_PRICE_TREND_STRONGLY_FALLING,
    MIN_PRICE_TREND_STRONGLY_RISING,
    MIN_RELAXATION_ATTEMPTS,
    MIN_VOLATILITY_THRESHOLD_HIGH,
    MIN_VOLATILITY_THRESHOLD_MODERATE,
    MIN_VOLATILITY_THRESHOLD_VERY_HIGH,
    PEAK_PRICE_MIN_LEVEL_OPTIONS,
    get_default_currency_display,
)
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.data_entry_flow import section
from homeassistant.helpers import selector
from homeassistant.helpers.selector import (
    BooleanSelector,
    ConstantSelector,
    ConstantSelectorConfig,
    NumberSelector,
    NumberSelectorConfig,
    NumberSelectorMode,
    SelectOptionDict,
    SelectSelector,
    SelectSelectorConfig,
    SelectSelectorMode,
    TextSelector,
    TextSelectorConfig,
    TextSelectorType,
)

# Type alias for config override structure: {section: {config_key: value}}
ConfigOverrides = dict[str, dict[str, Any]]


def is_field_overridden(
    config_key: str,
    config_section: str,
    overrides: ConfigOverrides | None,
) -> bool:
    """
    Check if a config field has an active runtime override.

    Args:
        config_key: The configuration key to check (e.g., "best_price_flex")
        config_section: Unused, kept for API compatibility
        overrides: Dictionary of active overrides (with "_enabled" key)

    Returns:
        True if this field is being overridden by a config entity, False otherwise

    """
    if overrides is None:
        return False
    # Check if key is in the _enabled section (from entity registry check)
    return config_key in overrides.get("_enabled", {})


# Override translations structure from common section
# This will be loaded at runtime and passed to schema functions
OverrideTranslations = dict[str, Any]  # Type alias

# Fallback labels when translations not available
# Used only as fallback - translations should be loaded from common.override_field_labels
DEFAULT_FIELD_LABELS: dict[str, str] = {
    # Best Price
    CONF_BEST_PRICE_MIN_PERIOD_LENGTH: "Minimum Period Length",
    CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT: "Gap Tolerance",
    CONF_BEST_PRICE_FLEX: "Flexibility",
    CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG: "Minimum Distance",
    CONF_ENABLE_MIN_PERIODS_BEST: "Achieve Minimum Count",
    CONF_MIN_PERIODS_BEST: "Minimum Periods",
    CONF_RELAXATION_ATTEMPTS_BEST: "Relaxation Attempts",
    # Peak Price
    CONF_PEAK_PRICE_MIN_PERIOD_LENGTH: "Minimum Period Length",
    CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT: "Gap Tolerance",
    CONF_PEAK_PRICE_FLEX: "Flexibility",
    CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG: "Minimum Distance",
    CONF_ENABLE_MIN_PERIODS_PEAK: "Achieve Minimum Count",
    CONF_MIN_PERIODS_PEAK: "Minimum Periods",
    CONF_RELAXATION_ATTEMPTS_PEAK: "Relaxation Attempts",
}

# Section to config keys mapping for override detection
SECTION_CONFIG_KEYS: dict[str, dict[str, list[str]]] = {
    "best_price": {
        "period_settings": [
            CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
            CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
        ],
        "flexibility_settings": [
            CONF_BEST_PRICE_FLEX,
            CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
        ],
        "relaxation_and_target_periods": [
            CONF_ENABLE_MIN_PERIODS_BEST,
            CONF_MIN_PERIODS_BEST,
            CONF_RELAXATION_ATTEMPTS_BEST,
        ],
    },
    "peak_price": {
        "period_settings": [
            CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
            CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
        ],
        "flexibility_settings": [
            CONF_PEAK_PRICE_FLEX,
            CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
        ],
        "relaxation_and_target_periods": [
            CONF_ENABLE_MIN_PERIODS_PEAK,
            CONF_MIN_PERIODS_PEAK,
            CONF_RELAXATION_ATTEMPTS_PEAK,
        ],
    },
}


def get_section_override_warning(
    step_id: str,
    section_id: str,
    overrides: ConfigOverrides | None,
    translations: OverrideTranslations | None = None,
) -> dict[vol.Optional, ConstantSelector] | None:
    """
    Return a warning constant selector if any fields in the section are overridden.

    Args:
        step_id: The step ID (best_price or peak_price)
        section_id: The section ID within the step
        overrides: Active runtime overrides from coordinator
        translations: Override translations from common section (optional)

    Returns:
        Dict with override warning selector if any fields overridden, None otherwise

    """
    if not overrides:
        return None

    section_keys = SECTION_CONFIG_KEYS.get(step_id, {}).get(section_id, [])
    overridden_fields = []

    for config_key in section_keys:
        if is_field_overridden(config_key, section_id, overrides):
            # Try to get translated label from flat keys, fallback to DEFAULT_FIELD_LABELS
            translation_key = f"override_field_label_{config_key}"
            label = (translations.get(translation_key) if translations else None) or DEFAULT_FIELD_LABELS.get(
                config_key, config_key
            )
            overridden_fields.append(label)

    if not overridden_fields:
        return None

    # Get translated "and" connector or use fallback
    and_connector = " and "
    if translations and "override_warning_and" in translations:
        and_connector = f" {translations['override_warning_and']} "

    # Build warning message with list of overridden fields
    if len(overridden_fields) == 1:
        fields_text = overridden_fields[0]
    else:
        fields_text = ", ".join(overridden_fields[:-1]) + and_connector + overridden_fields[-1]

    # Get translated warning template or use fallback
    warning_template = "⚠️ {fields} controlled by config entity"
    if translations and "override_warning_template" in translations:
        warning_template = translations["override_warning_template"]

    return {
        vol.Optional("_override_warning"): ConstantSelector(
            ConstantSelectorConfig(
                value=True,
                label=warning_template.format(fields=fields_text),
            )
        ),
    }


def get_user_schema(access_token: str | None = None) -> vol.Schema:
    """Return schema for user step (API token input)."""
    return vol.Schema(
        {
            vol.Required(
                CONF_ACCESS_TOKEN,
                default=access_token if access_token is not None else vol.UNDEFINED,
            ): TextSelector(
                TextSelectorConfig(
                    type=TextSelectorType.TEXT,
                ),
            ),
        }
    )


def get_reauth_confirm_schema() -> vol.Schema:
    """Return schema for reauth confirmation step."""
    return vol.Schema(
        {
            vol.Required(CONF_ACCESS_TOKEN): TextSelector(
                TextSelectorConfig(type=TextSelectorType.TEXT),
            ),
        }
    )


def get_select_home_schema(home_options: list[SelectOptionDict]) -> vol.Schema:
    """Return schema for home selection step."""
    return vol.Schema(
        {
            vol.Required("home_id"): SelectSelector(
                SelectSelectorConfig(
                    options=home_options,
                    mode=SelectSelectorMode.DROPDOWN,
                )
            )
        }
    )


def get_subentry_init_schema(
    *,
    extended_descriptions: bool = DEFAULT_EXTENDED_DESCRIPTIONS,
    offset_days: int = DEFAULT_VIRTUAL_TIME_OFFSET_DAYS,
    offset_hours: int = DEFAULT_VIRTUAL_TIME_OFFSET_HOURS,
    offset_minutes: int = DEFAULT_VIRTUAL_TIME_OFFSET_MINUTES,
) -> vol.Schema:
    """Return schema for subentry init step (includes time-travel settings)."""
    return vol.Schema(
        {
            vol.Optional(
                CONF_EXTENDED_DESCRIPTIONS,
                default=extended_descriptions,
            ): BooleanSelector(),
            vol.Optional(
                CONF_VIRTUAL_TIME_OFFSET_DAYS,
                default=offset_days,
            ): NumberSelector(
                NumberSelectorConfig(
                    mode=NumberSelectorMode.BOX,
                    min=-365,  # Max 1 year back
                    max=0,  # Only past days allowed
                    step=1,
                )
            ),
            vol.Optional(
                CONF_VIRTUAL_TIME_OFFSET_HOURS,
                default=offset_hours,
            ): NumberSelector(
                NumberSelectorConfig(
                    mode=NumberSelectorMode.BOX,
                    min=-23,
                    max=23,
                    step=1,
                )
            ),
            vol.Optional(
                CONF_VIRTUAL_TIME_OFFSET_MINUTES,
                default=offset_minutes,
            ): NumberSelector(
                NumberSelectorConfig(
                    mode=NumberSelectorMode.BOX,
                    min=-59,
                    max=59,
                    step=1,
                )
            ),
        }
    )


def get_options_init_schema(options: Mapping[str, Any]) -> vol.Schema:
    """Return schema for options init step (general settings)."""
    return vol.Schema(
        {
            vol.Optional(
                CONF_EXTENDED_DESCRIPTIONS,
                default=options.get(CONF_EXTENDED_DESCRIPTIONS, DEFAULT_EXTENDED_DESCRIPTIONS),
            ): BooleanSelector(),
            vol.Optional(
                CONF_AVERAGE_SENSOR_DISPLAY,
                default=str(
                    options.get(
                        CONF_AVERAGE_SENSOR_DISPLAY,
                        DEFAULT_AVERAGE_SENSOR_DISPLAY,
                    )
                ),
            ): SelectSelector(
                SelectSelectorConfig(
                    options=["median", "mean"],
                    mode=SelectSelectorMode.DROPDOWN,
                    translation_key="average_sensor_display",
                ),
            ),
        }
    )


def get_display_settings_schema(options: Mapping[str, Any], currency_code: str | None) -> vol.Schema:
    """Return schema for display settings configuration."""
    default_display_mode = get_default_currency_display(currency_code)

    return vol.Schema(
        {
            vol.Optional(
                CONF_CURRENCY_DISPLAY_MODE,
                default=str(
                    options.get(
                        CONF_CURRENCY_DISPLAY_MODE,
                        default_display_mode,
                    )
                ),
            ): SelectSelector(
                SelectSelectorConfig(
                    options=[DISPLAY_MODE_BASE, DISPLAY_MODE_SUBUNIT],
                    mode=SelectSelectorMode.DROPDOWN,
                    translation_key="currency_display_mode",
                ),
            ),
        }
    )


def get_price_rating_schema(options: Mapping[str, Any]) -> vol.Schema:
    """Return schema for price rating configuration (thresholds and stabilization)."""
    return vol.Schema(
        {
            vol.Optional(
                CONF_PRICE_RATING_THRESHOLD_LOW,
                default=int(
                    options.get(
                        CONF_PRICE_RATING_THRESHOLD_LOW,
                        DEFAULT_PRICE_RATING_THRESHOLD_LOW,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_RATING_THRESHOLD_LOW,
                    max=MAX_PRICE_RATING_THRESHOLD_LOW,
                    unit_of_measurement="%",
                    step=1,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_RATING_THRESHOLD_HIGH,
                default=int(
                    options.get(
                        CONF_PRICE_RATING_THRESHOLD_HIGH,
                        DEFAULT_PRICE_RATING_THRESHOLD_HIGH,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_RATING_THRESHOLD_HIGH,
                    max=MAX_PRICE_RATING_THRESHOLD_HIGH,
                    unit_of_measurement="%",
                    step=1,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_RATING_HYSTERESIS,
                default=float(
                    options.get(
                        CONF_PRICE_RATING_HYSTERESIS,
                        DEFAULT_PRICE_RATING_HYSTERESIS,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_RATING_HYSTERESIS,
                    max=MAX_PRICE_RATING_HYSTERESIS,
                    unit_of_measurement="%",
                    step=0.5,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_RATING_GAP_TOLERANCE,
                default=int(
                    options.get(
                        CONF_PRICE_RATING_GAP_TOLERANCE,
                        DEFAULT_PRICE_RATING_GAP_TOLERANCE,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_RATING_GAP_TOLERANCE,
                    max=MAX_PRICE_RATING_GAP_TOLERANCE,
                    step=1,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
        }
    )


def get_price_level_schema(options: Mapping[str, Any]) -> vol.Schema:
    """Return schema for Tibber price level stabilization (gap tolerance for API level field)."""
    return vol.Schema(
        {
            vol.Optional(
                CONF_PRICE_LEVEL_GAP_TOLERANCE,
                default=int(
                    options.get(
                        CONF_PRICE_LEVEL_GAP_TOLERANCE,
                        DEFAULT_PRICE_LEVEL_GAP_TOLERANCE,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_LEVEL_GAP_TOLERANCE,
                    max=MAX_PRICE_LEVEL_GAP_TOLERANCE,
                    step=1,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
        }
    )


def get_volatility_schema(options: Mapping[str, Any]) -> vol.Schema:
    """Return schema for volatility thresholds configuration."""
    return vol.Schema(
        {
            vol.Optional(
                CONF_VOLATILITY_THRESHOLD_MODERATE,
                default=float(
                    options.get(
                        CONF_VOLATILITY_THRESHOLD_MODERATE,
                        DEFAULT_VOLATILITY_THRESHOLD_MODERATE,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_VOLATILITY_THRESHOLD_MODERATE,
                    max=MAX_VOLATILITY_THRESHOLD_MODERATE,
                    step=1.0,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_VOLATILITY_THRESHOLD_HIGH,
                default=float(
                    options.get(
                        CONF_VOLATILITY_THRESHOLD_HIGH,
                        DEFAULT_VOLATILITY_THRESHOLD_HIGH,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_VOLATILITY_THRESHOLD_HIGH,
                    max=MAX_VOLATILITY_THRESHOLD_HIGH,
                    step=1.0,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
                default=float(
                    options.get(
                        CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
                        DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_VOLATILITY_THRESHOLD_VERY_HIGH,
                    max=MAX_VOLATILITY_THRESHOLD_VERY_HIGH,
                    step=1.0,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
        }
    )


def get_best_price_schema(
    options: Mapping[str, Any],
    overrides: ConfigOverrides | None = None,
    translations: OverrideTranslations | None = None,
) -> vol.Schema:
    """
    Return schema for best price period configuration with collapsible sections.

    Args:
        options: Current options from config entry
        overrides: Active runtime overrides from coordinator. Fields with active
                   overrides will be replaced with a constant placeholder.
        translations: Override translations from common section (optional)

    Returns:
        Voluptuous schema for the best price configuration form

    """
    period_settings = options.get("period_settings", {})
    flexibility_settings = options.get("flexibility_settings", {})
    relaxation_settings = options.get("relaxation_and_target_periods", {})
    extension_settings = options.get("extension_settings", {})

    # Get current values for override display
    min_period_length = int(
        period_settings.get(CONF_BEST_PRICE_MIN_PERIOD_LENGTH, DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH)
    )
    max_level_gap_count = int(
        period_settings.get(CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT, DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT)
    )
    best_price_flex = int(flexibility_settings.get(CONF_BEST_PRICE_FLEX, DEFAULT_BEST_PRICE_FLEX))
    min_distance = int(
        flexibility_settings.get(CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG)
    )
    enable_min_periods = relaxation_settings.get(CONF_ENABLE_MIN_PERIODS_BEST, DEFAULT_ENABLE_MIN_PERIODS_BEST)
    min_periods = int(relaxation_settings.get(CONF_MIN_PERIODS_BEST, DEFAULT_MIN_PERIODS_BEST))
    relaxation_attempts = int(relaxation_settings.get(CONF_RELAXATION_ATTEMPTS_BEST, DEFAULT_RELAXATION_ATTEMPTS_BEST))
    extend_to_very_cheap = bool(
        extension_settings.get(CONF_BEST_PRICE_EXTEND_TO_VERY_CHEAP, DEFAULT_BEST_PRICE_EXTEND_TO_VERY_CHEAP)
    )
    max_extension_intervals_best = int(
        extension_settings.get(CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS, DEFAULT_BEST_PRICE_MAX_EXTENSION_INTERVALS)
    )
    geometric_flex_best = int(extension_settings.get(CONF_BEST_PRICE_GEOMETRIC_FLEX, DEFAULT_BEST_PRICE_GEOMETRIC_FLEX))
    segment_forcing_best = bool(
        extension_settings.get(CONF_BEST_PRICE_SEGMENT_FORCING, DEFAULT_BEST_PRICE_SEGMENT_FORCING)
    )
    segment_min_periods_best = int(
        extension_settings.get(CONF_BEST_PRICE_SEGMENT_MIN_PERIODS, DEFAULT_BEST_PRICE_SEGMENT_MIN_PERIODS)
    )

    # Build section schemas with optional override warnings
    period_warning = get_section_override_warning("best_price", "period_settings", overrides, translations) or {}
    period_fields: dict[vol.Optional | vol.Required, Any] = {
        **period_warning,  # type: ignore[misc]
        vol.Optional(
            CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
            default=min_period_length,
        ): NumberSelector(
            NumberSelectorConfig(
                min=MIN_PERIOD_LENGTH,
                max=MAX_MIN_PERIOD_LENGTH,
                step=15,
                unit_of_measurement="min",
                mode=NumberSelectorMode.SLIDER,
            )
        ),
        vol.Optional(
            CONF_BEST_PRICE_MAX_LEVEL,
            default=period_settings.get(
                CONF_BEST_PRICE_MAX_LEVEL,
                DEFAULT_BEST_PRICE_MAX_LEVEL,
            ),
        ): SelectSelector(
            SelectSelectorConfig(
                options=BEST_PRICE_MAX_LEVEL_OPTIONS,
                mode=SelectSelectorMode.DROPDOWN,
                translation_key="current_interval_price_level",
            ),
        ),
        vol.Optional(
            CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
            default=max_level_gap_count,
        ): NumberSelector(
            NumberSelectorConfig(
                min=MIN_GAP_COUNT,
                max=MAX_GAP_COUNT,
                step=1,
                mode=NumberSelectorMode.SLIDER,
            )
        ),
    }

    flexibility_warning = (
        get_section_override_warning("best_price", "flexibility_settings", overrides, translations) or {}
    )
    flexibility_fields: dict[vol.Optional | vol.Required, Any] = {
        **flexibility_warning,  # type: ignore[misc]
        vol.Optional(
            CONF_BEST_PRICE_FLEX,
            default=best_price_flex,
        ): NumberSelector(
            NumberSelectorConfig(
                min=0,
                max=50,
                step=1,
                unit_of_measurement="%",
                mode=NumberSelectorMode.SLIDER,
            )
        ),
        vol.Optional(
            CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
            default=min_distance,
        ): NumberSelector(
            NumberSelectorConfig(
                min=-50,
                max=0,
                step=1,
                unit_of_measurement="%",
                mode=NumberSelectorMode.SLIDER,
            )
        ),
    }

    relaxation_warning = (
        get_section_override_warning("best_price", "relaxation_and_target_periods", overrides, translations) or {}
    )
    relaxation_fields: dict[vol.Optional | vol.Required, Any] = {
        **relaxation_warning,  # type: ignore[misc]
        vol.Optional(
            CONF_ENABLE_MIN_PERIODS_BEST,
            default=enable_min_periods,
        ): BooleanSelector(selector.BooleanSelectorConfig()),
        vol.Optional(
            CONF_MIN_PERIODS_BEST,
            default=min_periods,
        ): NumberSelector(
            NumberSelectorConfig(
                min=1,
                max=MAX_MIN_PERIODS,
                step=1,
                mode=NumberSelectorMode.SLIDER,
            )
        ),
        vol.Optional(
            CONF_RELAXATION_ATTEMPTS_BEST,
            default=relaxation_attempts,
        ): NumberSelector(
            NumberSelectorConfig(
                min=MIN_RELAXATION_ATTEMPTS,
                max=MAX_RELAXATION_ATTEMPTS,
                step=1,
                mode=NumberSelectorMode.SLIDER,
            )
        ),
    }

    return vol.Schema(
        {
            vol.Required("period_settings"): section(
                vol.Schema(period_fields),
                {"collapsed": False},
            ),
            vol.Required("flexibility_settings"): section(
                vol.Schema(flexibility_fields),
                {"collapsed": True},
            ),
            vol.Required("relaxation_and_target_periods"): section(
                vol.Schema(relaxation_fields),
                {"collapsed": True},
            ),
            vol.Required("extension_settings"): section(
                vol.Schema(
                    {
                        vol.Optional(
                            CONF_BEST_PRICE_EXTEND_TO_VERY_CHEAP,
                            default=extend_to_very_cheap,
                        ): BooleanSelector(selector.BooleanSelectorConfig()),
                        vol.Optional(
                            CONF_BEST_PRICE_MAX_EXTENSION_INTERVALS,
                            default=max_extension_intervals_best,
                        ): NumberSelector(
                            NumberSelectorConfig(
                                min=1,
                                max=MAX_EXTENSION_INTERVALS,
                                step=1,
                                mode=NumberSelectorMode.SLIDER,
                            )
                        ),
                        vol.Optional(
                            CONF_BEST_PRICE_GEOMETRIC_FLEX,
                            default=geometric_flex_best,
                        ): NumberSelector(
                            NumberSelectorConfig(
                                min=0,
                                max=MAX_GEOMETRIC_FLEX,
                                step=1,
                                unit_of_measurement="%",
                                mode=NumberSelectorMode.SLIDER,
                            )
                        ),
                        vol.Optional(
                            CONF_BEST_PRICE_SEGMENT_FORCING,
                            default=segment_forcing_best,
                        ): BooleanSelector(selector.BooleanSelectorConfig()),
                        vol.Optional(
                            CONF_BEST_PRICE_SEGMENT_MIN_PERIODS,
                            default=segment_min_periods_best,
                        ): NumberSelector(
                            NumberSelectorConfig(
                                min=1,
                                max=MAX_SEGMENT_MIN_PERIODS,
                                step=1,
                                mode=NumberSelectorMode.SLIDER,
                            )
                        ),
                    }
                ),
                {"collapsed": True},
            ),
        }
    )


def get_peak_price_schema(
    options: Mapping[str, Any],
    overrides: ConfigOverrides | None = None,
    translations: OverrideTranslations | None = None,
) -> vol.Schema:
    """
    Return schema for peak price period configuration with collapsible sections.

    Args:
        options: Current options from config entry
        overrides: Active runtime overrides from coordinator. Fields with active
                   overrides will be replaced with a constant placeholder.
        translations: Override translations from common section (optional)

    Returns:
        Voluptuous schema for the peak price configuration form

    """
    period_settings = options.get("period_settings", {})
    flexibility_settings = options.get("flexibility_settings", {})
    relaxation_settings = options.get("relaxation_and_target_periods", {})
    extension_settings = options.get("extension_settings", {})

    # Get current values for override display
    min_period_length = int(
        period_settings.get(CONF_PEAK_PRICE_MIN_PERIOD_LENGTH, DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH)
    )
    max_level_gap_count = int(
        period_settings.get(CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT, DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT)
    )
    peak_price_flex = int(flexibility_settings.get(CONF_PEAK_PRICE_FLEX, DEFAULT_PEAK_PRICE_FLEX))
    min_distance = int(
        flexibility_settings.get(CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG)
    )
    enable_min_periods = relaxation_settings.get(CONF_ENABLE_MIN_PERIODS_PEAK, DEFAULT_ENABLE_MIN_PERIODS_PEAK)
    min_periods = int(relaxation_settings.get(CONF_MIN_PERIODS_PEAK, DEFAULT_MIN_PERIODS_PEAK))
    relaxation_attempts = int(relaxation_settings.get(CONF_RELAXATION_ATTEMPTS_PEAK, DEFAULT_RELAXATION_ATTEMPTS_PEAK))
    extend_to_very_expensive = bool(
        extension_settings.get(CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE, DEFAULT_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE)
    )
    max_extension_intervals_peak = int(
        extension_settings.get(CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS, DEFAULT_PEAK_PRICE_MAX_EXTENSION_INTERVALS)
    )
    geometric_flex_peak = int(extension_settings.get(CONF_PEAK_PRICE_GEOMETRIC_FLEX, DEFAULT_PEAK_PRICE_GEOMETRIC_FLEX))
    segment_forcing_peak = bool(
        extension_settings.get(CONF_PEAK_PRICE_SEGMENT_FORCING, DEFAULT_PEAK_PRICE_SEGMENT_FORCING)
    )
    segment_min_periods_peak = int(
        extension_settings.get(CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS, DEFAULT_PEAK_PRICE_SEGMENT_MIN_PERIODS)
    )

    # Build section schemas with optional override warnings
    period_warning = get_section_override_warning("peak_price", "period_settings", overrides, translations) or {}
    period_fields: dict[vol.Optional | vol.Required, Any] = {
        **period_warning,  # type: ignore[misc]
        vol.Optional(
            CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
            default=min_period_length,
        ): NumberSelector(
            NumberSelectorConfig(
                min=MIN_PERIOD_LENGTH,
                max=MAX_MIN_PERIOD_LENGTH,
                step=15,
                unit_of_measurement="min",
                mode=NumberSelectorMode.SLIDER,
            )
        ),
        vol.Optional(
            CONF_PEAK_PRICE_MIN_LEVEL,
            default=period_settings.get(
                CONF_PEAK_PRICE_MIN_LEVEL,
                DEFAULT_PEAK_PRICE_MIN_LEVEL,
            ),
        ): SelectSelector(
            SelectSelectorConfig(
                options=PEAK_PRICE_MIN_LEVEL_OPTIONS,
                mode=SelectSelectorMode.DROPDOWN,
                translation_key="current_interval_price_level",
            ),
        ),
        vol.Optional(
            CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
            default=max_level_gap_count,
        ): NumberSelector(
            NumberSelectorConfig(
                min=MIN_GAP_COUNT,
                max=MAX_GAP_COUNT,
                step=1,
                mode=NumberSelectorMode.SLIDER,
            )
        ),
    }

    flexibility_warning = (
        get_section_override_warning("peak_price", "flexibility_settings", overrides, translations) or {}
    )
    flexibility_fields: dict[vol.Optional | vol.Required, Any] = {
        **flexibility_warning,  # type: ignore[misc]
        vol.Optional(
            CONF_PEAK_PRICE_FLEX,
            default=peak_price_flex,
        ): NumberSelector(
            NumberSelectorConfig(
                min=-50,
                max=0,
                step=1,
                unit_of_measurement="%",
                mode=NumberSelectorMode.SLIDER,
            )
        ),
        vol.Optional(
            CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
            default=min_distance,
        ): NumberSelector(
            NumberSelectorConfig(
                min=0,
                max=50,
                step=1,
                unit_of_measurement="%",
                mode=NumberSelectorMode.SLIDER,
            )
        ),
    }

    relaxation_warning = (
        get_section_override_warning("peak_price", "relaxation_and_target_periods", overrides, translations) or {}
    )
    relaxation_fields: dict[vol.Optional | vol.Required, Any] = {
        **relaxation_warning,  # type: ignore[misc]
        vol.Optional(
            CONF_ENABLE_MIN_PERIODS_PEAK,
            default=enable_min_periods,
        ): BooleanSelector(selector.BooleanSelectorConfig()),
        vol.Optional(
            CONF_MIN_PERIODS_PEAK,
            default=min_periods,
        ): NumberSelector(
            NumberSelectorConfig(
                min=1,
                max=MAX_MIN_PERIODS,
                step=1,
                mode=NumberSelectorMode.SLIDER,
            )
        ),
        vol.Optional(
            CONF_RELAXATION_ATTEMPTS_PEAK,
            default=relaxation_attempts,
        ): NumberSelector(
            NumberSelectorConfig(
                min=MIN_RELAXATION_ATTEMPTS,
                max=MAX_RELAXATION_ATTEMPTS,
                step=1,
                mode=NumberSelectorMode.SLIDER,
            )
        ),
    }

    return vol.Schema(
        {
            vol.Required("period_settings"): section(
                vol.Schema(period_fields),
                {"collapsed": False},
            ),
            vol.Required("flexibility_settings"): section(
                vol.Schema(flexibility_fields),
                {"collapsed": True},
            ),
            vol.Required("relaxation_and_target_periods"): section(
                vol.Schema(relaxation_fields),
                {"collapsed": True},
            ),
            vol.Required("extension_settings"): section(
                vol.Schema(
                    {
                        vol.Optional(
                            CONF_PEAK_PRICE_EXTEND_TO_VERY_EXPENSIVE,
                            default=extend_to_very_expensive,
                        ): BooleanSelector(selector.BooleanSelectorConfig()),
                        vol.Optional(
                            CONF_PEAK_PRICE_MAX_EXTENSION_INTERVALS,
                            default=max_extension_intervals_peak,
                        ): NumberSelector(
                            NumberSelectorConfig(
                                min=1,
                                max=MAX_EXTENSION_INTERVALS,
                                step=1,
                                mode=NumberSelectorMode.SLIDER,
                            )
                        ),
                        vol.Optional(
                            CONF_PEAK_PRICE_GEOMETRIC_FLEX,
                            default=geometric_flex_peak,
                        ): NumberSelector(
                            NumberSelectorConfig(
                                min=0,
                                max=MAX_GEOMETRIC_FLEX,
                                step=1,
                                unit_of_measurement="%",
                                mode=NumberSelectorMode.SLIDER,
                            )
                        ),
                        vol.Optional(
                            CONF_PEAK_PRICE_SEGMENT_FORCING,
                            default=segment_forcing_peak,
                        ): BooleanSelector(selector.BooleanSelectorConfig()),
                        vol.Optional(
                            CONF_PEAK_PRICE_SEGMENT_MIN_PERIODS,
                            default=segment_min_periods_peak,
                        ): NumberSelector(
                            NumberSelectorConfig(
                                min=1,
                                max=MAX_SEGMENT_MIN_PERIODS,
                                step=1,
                                mode=NumberSelectorMode.SLIDER,
                            )
                        ),
                    }
                ),
                {"collapsed": True},
            ),
        }
    )


def get_price_trend_schema(
    options: Mapping[str, Any],
    *,
    display_factor: int = 1,
    price_unit: str = "",
) -> vol.Schema:
    """Return schema for price trend thresholds configuration."""
    # Scale min_price_change values for display (stored in base currency, shown in display unit)
    step = 0.1 if display_factor > 1 else 0.001

    return vol.Schema(
        {
            vol.Optional(
                CONF_PRICE_TREND_THRESHOLD_RISING,
                default=int(
                    options.get(
                        CONF_PRICE_TREND_THRESHOLD_RISING,
                        DEFAULT_PRICE_TREND_THRESHOLD_RISING,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_RISING,
                    max=MAX_PRICE_TREND_RISING,
                    step=1,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_TREND_THRESHOLD_STRONGLY_RISING,
                default=int(
                    options.get(
                        CONF_PRICE_TREND_THRESHOLD_STRONGLY_RISING,
                        DEFAULT_PRICE_TREND_THRESHOLD_STRONGLY_RISING,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_STRONGLY_RISING,
                    max=MAX_PRICE_TREND_STRONGLY_RISING,
                    step=1,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_TREND_THRESHOLD_FALLING,
                default=int(
                    options.get(
                        CONF_PRICE_TREND_THRESHOLD_FALLING,
                        DEFAULT_PRICE_TREND_THRESHOLD_FALLING,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_FALLING,
                    max=MAX_PRICE_TREND_FALLING,
                    step=1,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_TREND_THRESHOLD_STRONGLY_FALLING,
                default=int(
                    options.get(
                        CONF_PRICE_TREND_THRESHOLD_STRONGLY_FALLING,
                        DEFAULT_PRICE_TREND_THRESHOLD_STRONGLY_FALLING,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_STRONGLY_FALLING,
                    max=MAX_PRICE_TREND_STRONGLY_FALLING,
                    step=1,
                    unit_of_measurement="%",
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_TREND_CHANGE_CONFIRMATION,
                default=int(
                    options.get(
                        CONF_PRICE_TREND_CHANGE_CONFIRMATION,
                        DEFAULT_PRICE_TREND_CHANGE_CONFIRMATION,
                    )
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_CHANGE_CONFIRMATION,
                    max=MAX_PRICE_TREND_CHANGE_CONFIRMATION,
                    step=1,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_TREND_MIN_PRICE_CHANGE,
                default=round(
                    float(
                        options.get(
                            CONF_PRICE_TREND_MIN_PRICE_CHANGE,
                            DEFAULT_PRICE_TREND_MIN_PRICE_CHANGE,
                        )
                    )
                    * display_factor,
                    3,
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_MIN_PRICE_CHANGE * display_factor,
                    max=MAX_PRICE_TREND_MIN_PRICE_CHANGE * display_factor,
                    step=step,
                    unit_of_measurement=price_unit,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
            vol.Optional(
                CONF_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
                default=round(
                    float(
                        options.get(
                            CONF_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
                            DEFAULT_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
                        )
                    )
                    * display_factor,
                    3,
                ),
            ): NumberSelector(
                NumberSelectorConfig(
                    min=MIN_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY * display_factor,
                    max=MAX_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY * display_factor,
                    step=step,
                    unit_of_measurement=price_unit,
                    mode=NumberSelectorMode.SLIDER,
                ),
            ),
        }
    )


def get_chart_data_export_schema(_options: Mapping[str, Any]) -> vol.Schema:
    """Return schema for chart data export info page (no input fields)."""
    # Empty schema - this is just an info page now
    return vol.Schema({})


def get_reset_to_defaults_schema() -> vol.Schema:
    """Return schema for reset to defaults confirmation step."""
    return vol.Schema(
        {
            vol.Required("confirm_reset", default=False): selector.BooleanSelector(),
        }
    )
