"""Test midnight turnover consistency - period visibility before/after midnight."""

from __future__ import annotations

from datetime import datetime, timedelta
from unittest.mock import Mock
from zoneinfo import ZoneInfo

import pytest

from custom_components.tibber_prices.coordinator.period_handlers.core import calculate_periods
from custom_components.tibber_prices.coordinator.period_handlers.types import TibberPricesPeriodConfig
from custom_components.tibber_prices.coordinator.time_service import TibberPricesTimeService


def create_price_interval(dt: datetime, price: float) -> dict:
    """Create a price interval dict."""
    return {
        "startsAt": dt,
        "total": price,
        "level": "NORMAL",
        "rating_level": "NORMAL",
    }


def create_price_data_scenario() -> tuple[list[dict], list[dict], list[dict], list[dict]]:
    """Create a realistic price scenario with a period crossing midnight."""
    tz = ZoneInfo("Europe/Berlin")
    base = datetime(2025, 11, 21, 0, 0, 0, tzinfo=tz)

    # Define cheap hour ranges for each day
    cheap_hours = {
        "yesterday": range(22, 24),  # 22:00-23:45
        "today": range(21, 24),  # 21:00-23:45 (crosses midnight!)
        "tomorrow": range(1),  # 00:00-00:45 (continuation)
    }

    def generate_day_prices(day_dt: datetime, cheap_range: range) -> list[dict]:
        """Generate 15-min interval prices for a day."""
        prices = []
        for hour in range(24):
            for minute in [0, 15, 30, 45]:
                dt = day_dt.replace(hour=hour, minute=minute)
                price = 15.0 if hour in cheap_range else 30.0
                prices.append(create_price_interval(dt, price))
        return prices

    yesterday_prices = generate_day_prices(base - timedelta(days=1), cheap_hours["yesterday"])
    today_prices = generate_day_prices(base, cheap_hours["today"])
    tomorrow_prices = generate_day_prices(base + timedelta(days=1), cheap_hours["tomorrow"])
    day_after_tomorrow_prices = generate_day_prices(base + timedelta(days=2), range(0))  # No cheap hours

    return yesterday_prices, today_prices, tomorrow_prices, day_after_tomorrow_prices


@pytest.fixture
def period_config() -> TibberPricesPeriodConfig:
    """Provide test period configuration."""
    return TibberPricesPeriodConfig(
        reverse_sort=False,  # Best price (cheap periods)
        flex=0.50,  # 50% flexibility
        min_distance_from_avg=-5.0,  # -5% below average
        min_period_length=60,  # 60 minutes minimum
        threshold_low=20.0,
        threshold_high=30.0,
        threshold_volatility_moderate=0.3,
        threshold_volatility_high=0.5,
        threshold_volatility_very_high=0.7,
        level_filter=None,
        gap_count=0,
    )


@pytest.mark.integration
def test_midnight_crossing_period_consistency(period_config: TibberPricesPeriodConfig) -> None:
    """
    Test that midnight-crossing periods remain visible before and after midnight turnover.

    This test simulates the real-world scenario where:
    - Before midnight (21st 22:00): Period 21:00→01:00 is visible
    - After midnight (22nd 00:30): Same period should still be visible

    The period starts on 2025-11-21 (yesterday after turnover) and ends on 2025-11-22 (today).
    """
    tz = ZoneInfo("Europe/Berlin")
    yesterday_prices, today_prices, tomorrow_prices, day_after_tomorrow_prices = create_price_data_scenario()

    # Create mock config entry
    mock_config_entry = Mock()
    mock_config_entry.options.get.return_value = "minor"

    # SCENARIO 1: Before midnight (today = 2025-11-21 22:00)
    current_time_before = datetime(2025, 11, 21, 22, 0, 0, tzinfo=tz)
    time_service_before = TibberPricesTimeService(current_time_before)
    all_prices_before = yesterday_prices + today_prices + tomorrow_prices

    result_before = calculate_periods(
        all_prices_before,
        config=period_config,
        time=time_service_before,
    )
    periods_before = result_before["periods"]

    # Find the midnight-crossing period (starts 21st, ends 22nd)
    midnight_period_before = None
    for period in periods_before:
        if period["start"].date().isoformat() == "2025-11-21" and period["end"].date().isoformat() == "2025-11-22":
            midnight_period_before = period
            break

    assert midnight_period_before is not None, "Expected to find midnight-crossing period before turnover"

    # SCENARIO 2: After midnight turnover (today = 2025-11-22 00:30)
    current_time_after = datetime(2025, 11, 22, 0, 30, 0, tzinfo=tz)
    time_service_after = TibberPricesTimeService(current_time_after)

    # Simulate coordinator data shift: yesterday=21st, today=22nd, tomorrow=23rd
    yesterday_after_turnover = today_prices
    today_after_turnover = tomorrow_prices
    tomorrow_after_turnover = day_after_tomorrow_prices
    all_prices_after = yesterday_after_turnover + today_after_turnover + tomorrow_after_turnover

    result_after = calculate_periods(
        all_prices_after,
        config=period_config,
        time=time_service_after,
    )
    periods_after = result_after["periods"]

    # Find period that started on 2025-11-21 (now "yesterday")
    period_from_yesterday_after = None
    for period in periods_after:
        if period["start"].date().isoformat() == "2025-11-21":
            period_from_yesterday_after = period
            break

    assert period_from_yesterday_after is not None, (
        "Expected midnight-crossing period to remain visible after turnover (we're at 00:30, period ends at 01:00)"
    )

    # Verify consistency: same absolute times
    assert midnight_period_before["start"] == period_from_yesterday_after["start"], "Start time should match"
    assert midnight_period_before["end"] == period_from_yesterday_after["end"], "End time should match"
    assert midnight_period_before["duration_minutes"] == period_from_yesterday_after["duration_minutes"], (
        "Duration should match"
    )
