"""
Routing Module - API endpoint selection for price intervals.

This module handles intelligent routing between different Tibber API endpoints:

- PRICE_INFO: Recent data (from "day before yesterday midnight" onwards)
- PRICE_INFO_RANGE: Historical data (before "day before yesterday midnight")
- Automatic splitting and merging when range spans the boundary

CRITICAL: Uses REAL TIME (dt_util.now()) for API boundary calculation,
NOT TimeService.now() which may be shifted for internal simulation.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

from custom_components.tibber_prices.api.exceptions import TibberPricesApiClientError
from homeassistant.util import dt as dt_util

if TYPE_CHECKING:
    from datetime import datetime

    from custom_components.tibber_prices.api.client import TibberPricesApiClient

_LOGGER = logging.getLogger(__name__)
_LOGGER_DETAILS = logging.getLogger(__name__ + ".details")


async def get_price_intervals_for_range(
    api_client: TibberPricesApiClient,
    home_id: str,
    user_data: dict[str, Any],
    start_time: datetime,
    end_time: datetime,
) -> list[dict[str, Any]]:
    """
    Get price intervals for a specific time range with automatic routing.

    Automatically routes to the correct API endpoint based on the time range:
    - PRICE_INFO_RANGE: For intervals exclusively before "day before yesterday midnight" (real time)
    - PRICE_INFO: For intervals from "day before yesterday midnight" onwards
    - Both: If range spans across the boundary, splits the request

    CRITICAL: Uses REAL TIME (dt_util.now()) for API boundary calculation,
    NOT TimeService.now() which may be shifted for internal simulation.
    This ensures predictable API responses.

    CACHING STRATEGY: Returns ALL intervals from API response, NOT filtered.
    The caller (pool.py) will cache everything and then filter to user request.
    This maximizes cache efficiency - one API call can populate cache for
    multiple subsequent queries.

    Args:
        api_client: TibberPricesApiClient instance for API calls.
        home_id: Home ID to fetch price data for.
        user_data: User data dict containing home metadata (including timezone).
        start_time: Start of the range (inclusive, timezone-aware).
        end_time: End of the range (exclusive, timezone-aware).

    Returns:
        List of ALL price interval dicts from API (unfiltered).
        - PRICE_INFO: Returns ~384 intervals (day-before-yesterday to tomorrow)
        - PRICE_INFO_RANGE: Returns intervals for requested historical range
        - Both: Returns all intervals from both endpoints

    Raises:
        TibberPricesApiClientError: If arguments invalid or requests fail.

    """
    if not user_data:
        msg = "User data required for timezone-aware price fetching - fetch user data first"
        raise TibberPricesApiClientError(msg)

    if not home_id:
        msg = "Home ID is required"
        raise TibberPricesApiClientError(msg)

    if start_time >= end_time:
        msg = f"Invalid time range: start_time ({start_time}) must be before end_time ({end_time})"
        raise TibberPricesApiClientError(msg)

    # Calculate boundary: day before yesterday midnight (REAL TIME, not TimeService)
    boundary = _calculate_boundary(api_client, user_data, home_id)

    _LOGGER_DETAILS.debug(
        "Routing price interval request for home %s: range %s to %s, boundary %s",
        home_id,
        start_time,
        end_time,
        boundary,
    )

    # Route based on time range
    if end_time <= boundary:
        # Entire range is historical (before day before yesterday) → use PRICE_INFO_RANGE
        _LOGGER_DETAILS.debug("Range is fully historical, using PRICE_INFO_RANGE")
        result = await api_client.async_get_price_info_range(
            home_id=home_id,
            user_data=user_data,
            start_time=start_time,
            end_time=end_time,
        )
        return result["price_info"]

    if start_time >= boundary:
        # Entire range is recent (from day before yesterday onwards) → use PRICE_INFO
        _LOGGER_DETAILS.debug("Range is fully recent, using PRICE_INFO")
        result = await api_client.async_get_price_info(home_id, user_data)

        # Return ALL intervals (unfiltered) for maximum cache efficiency
        # Pool will cache everything, then filter to user request
        return result["price_info"]

    # Range spans boundary → split request
    _LOGGER_DETAILS.debug("Range spans boundary, splitting request")

    # Fetch historical part (start_time to boundary)
    historical_result = await api_client.async_get_price_info_range(
        home_id=home_id,
        user_data=user_data,
        start_time=start_time,
        end_time=boundary,
    )

    # Fetch recent part (boundary onwards)
    recent_result = await api_client.async_get_price_info(home_id, user_data)

    # Return ALL intervals (unfiltered) for maximum cache efficiency
    # Pool will cache everything, then filter to user request
    return historical_result["price_info"] + recent_result["price_info"]


def _calculate_boundary(
    api_client: TibberPricesApiClient,
    user_data: dict[str, Any],
    home_id: str,
) -> datetime:
    """
    Calculate the API boundary (day before yesterday midnight).

    Uses the API client's helper method to extract timezone and calculate boundary.

    Args:
        api_client: TibberPricesApiClient instance.
        user_data: User data dict containing home metadata.
        home_id: Home ID to get timezone for.

    Returns:
        Timezone-aware datetime for day before yesterday midnight.

    """
    # Extract timezone for this home
    home_timezones = api_client._extract_home_timezones(user_data)  # noqa: SLF001
    home_tz = home_timezones.get(home_id)

    # Calculate boundary using API client's method
    return api_client._calculate_day_before_yesterday_midnight(home_tz)  # noqa: SLF001


def _parse_timestamp(timestamp_str: str) -> datetime:
    """
    Parse ISO timestamp string to timezone-aware datetime.

    Args:
        timestamp_str: ISO format timestamp string.

    Returns:
        Timezone-aware datetime object.

    Raises:
        ValueError: If timestamp string cannot be parsed.

    """
    result = dt_util.parse_datetime(timestamp_str)
    if result is None:
        msg = f"Failed to parse timestamp: {timestamp_str}"
        raise ValueError(msg)
    return result
