"""Pure utility functions for coordinator module."""

from __future__ import annotations

from datetime import timedelta
import logging
from typing import TYPE_CHECKING, Any

from homeassistant.util import dt as dt_util

if TYPE_CHECKING:
    from .time_service import TibberPricesTimeService

_LOGGER = logging.getLogger(__name__)


def get_intervals_for_day_offsets(
    coordinator_data: dict[str, Any] | None,
    offsets: list[int],
) -> list[dict[str, Any]]:
    """
    Get intervals for specific day offsets from coordinator data.

    This is the core function for filtering intervals by date offset.
    Abstracts the data structure - callers don't need to know where intervals are stored.

    Performance optimized:
    - Date comparison using .date() on datetime objects (fast)
    - Single pass through intervals with date caching
    - Only processes requested offsets

    Args:
        coordinator_data: Coordinator data dict (typically coordinator.data).
        offsets: List of day offsets relative to today (e.g., [0, 1] for today and tomorrow).
                 Range: -374 to +1 (allows historical comparisons up to one year + one week).
                 0 = today, -1 = yesterday, +1 = tomorrow, -7 = one week ago, etc.

    Returns:
        List of intervals matching the requested day offsets, in chronological order.

    Example:
        # Get only today's intervals
        today_intervals = get_intervals_for_day_offsets(coordinator.data, [0])

        # Get today and tomorrow
        future_intervals = get_intervals_for_day_offsets(coordinator.data, [0, 1])

        # Get all available intervals
        all = get_intervals_for_day_offsets(coordinator.data, [-1, 0, 1])

        # Compare last week with same week one year ago
        comparison = get_intervals_for_day_offsets(coordinator.data, [-7, -371])

    """
    if not coordinator_data:
        return []

    # Validate offsets are within acceptable range
    min_offset = -374  # One year + one week for comparisons
    max_offset = 1  # Tomorrow (we don't have data further in the future)

    # Extract intervals from coordinator data structure (priceInfo is now a list)
    all_intervals = coordinator_data.get("priceInfo", [])

    if not all_intervals:
        return []

    # Get current local date for comparison (no TimeService needed - use dt_util directly)
    now_local = dt_util.now()
    today_date = now_local.date()

    # Build set of target dates based on requested offsets
    target_dates = set()
    for offset in offsets:
        # Silently clamp offsets to valid range (don't fail on invalid input)
        if offset < min_offset or offset > max_offset:
            continue
        target_date = today_date + timedelta(days=offset)
        target_dates.add(target_date)

    if not target_dates:
        return []

    # Filter intervals matching target dates
    # Optimized: single pass, date() called once per interval
    result = []
    for interval in all_intervals:
        starts_at = interval.get("startsAt")
        if not starts_at:
            continue

        # Handle both datetime objects and strings (for flexibility)
        if isinstance(starts_at, str):
            # Parse if string (should be rare after parse_all_timestamps)
            starts_at = dt_util.parse_datetime(starts_at)
            if not starts_at:
                continue
            starts_at = dt_util.as_local(starts_at)

        # Fast date comparison using datetime.date()
        interval_date = starts_at.date()
        if interval_date in target_dates:
            result.append(interval)

    return result


def needs_tomorrow_data(
    cached_price_data: dict[str, Any] | None,
) -> bool:
    """
    Check if tomorrow data is missing or invalid in cached price data.

    Expects single-home cache format: {"price_info": [...], "home_id": "xxx"}

    Old multi-home format (v0.14.0) is automatically invalidated by is_cache_valid()
    in cache.py, so we only need to handle the current format here.

    Uses get_intervals_for_day_offsets() to automatically determine tomorrow
    based on current date. No explicit date parameter needed.

    Args:
        cached_price_data: Cached price data in single-home structure

    Returns:
        True if tomorrow's data is missing, False otherwise

    """
    if not cached_price_data or "price_info" not in cached_price_data:
        return False

    # Single-home format: {"price_info": [...], "home_id": "xxx"}
    # Use helper to get tomorrow's intervals (offset +1 from current date)
    coordinator_data = {"priceInfo": cached_price_data.get("price_info", [])}
    tomorrow_intervals = get_intervals_for_day_offsets(coordinator_data, [1])

    # If no intervals for tomorrow found, we need tomorrow data
    return len(tomorrow_intervals) == 0


def parse_all_timestamps(price_data: dict[str, Any], *, time: TibberPricesTimeService) -> dict[str, Any]:
    """
    Parse all API timestamp strings to datetime objects.

    This is the SINGLE place where we convert API strings to datetime objects.
    After this, all code works with datetime objects, not strings.

    Performance: ~200 timestamps parsed ONCE instead of multiple times per update cycle.

    Args:
        price_data: Raw API data with string timestamps (single-home structure)
        time: TibberPricesTimeService for parsing

    Returns:
        Same structure but with datetime objects instead of strings

    """
    if not price_data:
        return price_data

    # Single-home structure: price_info is a flat list of intervals
    price_info = price_data.get("price_info", [])

    # Skip if price_info is not a list (empty or invalid)
    if not isinstance(price_info, list):
        return price_data

    # Parse timestamps in flat interval list
    for interval in price_info:
        if (starts_at_str := interval.get("startsAt")) and isinstance(starts_at_str, str):
            # Parse once, convert to local timezone, store as datetime object
            interval["startsAt"] = time.parse_and_localize(starts_at_str)
            # If already datetime (e.g., from cache), skip parsing

    return price_data
