"""Test period data formatting for ApexCharts visualization."""

from datetime import UTC, datetime


def test_period_array_of_arrays_with_insert_nulls() -> None:
    """
    Test that period data generates 3 points per period when insert_nulls='segments'.

    For ApexCharts to correctly display periods as continuous blocks:
    1. Start time with price - Begin the period
    2. End time with price - Hold the price level until end
    3. End time with NULL - Cleanly terminate the segment (only with insert_nulls)
    """
    # Simulate a period from formatters.get_period_data()
    period = {
        "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
        "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
        "price_median": 12.50,  # Stored in major units (12.50 EUR)
        "level": "CHEAP",
        "rating_level": "LOW",
    }

    # Test with insert_nulls='segments' (should add NULL terminator)
    chart_data = []
    price_median = period["price_median"]
    start_serialized = period["start"].isoformat()
    end_serialized = period["end"].isoformat()
    insert_nulls = "segments"

    chart_data.append([start_serialized, price_median])  # 1. Start with price
    chart_data.append([end_serialized, price_median])  # 2. End with price (hold level)
    # 3. Add NULL terminator only if insert_nulls is enabled
    if insert_nulls in ("segments", "all"):
        chart_data.append([end_serialized, None])  # 3. End with NULL (terminate segment)

    # Verify structure
    assert len(chart_data) == 3, "Should generate 3 points with insert_nulls='segments'"

    # Point 1: Start with price
    assert chart_data[0][0] == "2025-12-03T10:00:00+00:00"
    assert chart_data[0][1] == 12.50

    # Point 2: End with price (holds level)
    assert chart_data[1][0] == "2025-12-03T12:00:00+00:00"
    assert chart_data[1][1] == 12.50

    # Point 3: End with NULL (terminates segment)
    assert chart_data[2][0] == "2025-12-03T12:00:00+00:00"
    assert chart_data[2][1] is None


def test_period_array_of_arrays_without_insert_nulls() -> None:
    """
    Test that period data generates 2 points per period when insert_nulls='none'.

    Without NULL insertion, we only get:
    1. Start time with price
    2. End time with price
    """
    period = {
        "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
        "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
        "price_median": 12.50,
    }

    # Test with insert_nulls='none' (should NOT add NULL terminator)
    chart_data = []
    price_median = period["price_median"]
    start_serialized = period["start"].isoformat()
    end_serialized = period["end"].isoformat()
    insert_nulls = "none"

    chart_data.append([start_serialized, price_median])
    chart_data.append([end_serialized, price_median])
    if insert_nulls in ("segments", "all"):
        chart_data.append([end_serialized, None])

    # Verify structure: Only 2 points without NULL terminator
    assert len(chart_data) == 2, "Should generate 2 points with insert_nulls='none'"
    assert chart_data[0][1] == 12.50
    assert chart_data[1][1] == 12.50


def test_multiple_periods_separated_by_nulls() -> None:
    """
    Test that multiple periods are properly separated by NULL points with insert_nulls enabled.

    This ensures gaps between periods are visualized correctly in ApexCharts.
    """
    periods = [
        {
            "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
            "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
            "price_median": 12.50,
        },
        {
            "start": datetime(2025, 12, 3, 15, 0, tzinfo=UTC),
            "end": datetime(2025, 12, 3, 17, 0, tzinfo=UTC),
            "price_median": 18.50,
        },
    ]

    chart_data = []
    insert_nulls = "segments"
    for period in periods:
        price_median = period["price_median"]
        start_serialized = period["start"].isoformat()
        end_serialized = period["end"].isoformat()

        chart_data.append([start_serialized, price_median])
        chart_data.append([end_serialized, price_median])
        if insert_nulls in ("segments", "all"):
            chart_data.append([end_serialized, None])

    # Verify structure: 2 periods x 3 points = 6 total points (with insert_nulls)
    assert len(chart_data) == 6, "Should generate 6 points for 2 periods with insert_nulls"

    # Period 1 ends with NULL
    assert chart_data[2][1] is None

    # Period 2 starts
    assert chart_data[3][0] == "2025-12-03T15:00:00+00:00"
    assert chart_data[3][1] == 18.50

    # Period 2 ends with NULL
    assert chart_data[5][1] is None


def test_multiple_periods_without_nulls() -> None:
    """
    Test that multiple periods without insert_nulls generate continuous data.

    Without NULL separators, periods connect directly (may be desired for some chart types).
    """
    periods = [
        {
            "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
            "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
            "price_median": 12.50,
        },
        {
            "start": datetime(2025, 12, 3, 15, 0, tzinfo=UTC),
            "end": datetime(2025, 12, 3, 17, 0, tzinfo=UTC),
            "price_median": 18.50,
        },
    ]

    chart_data = []
    insert_nulls = "none"
    for period in periods:
        price_median = period["price_median"]
        start_serialized = period["start"].isoformat()
        end_serialized = period["end"].isoformat()

        chart_data.append([start_serialized, price_median])
        chart_data.append([end_serialized, price_median])
        if insert_nulls in ("segments", "all"):
            chart_data.append([end_serialized, None])

    # Verify structure: 2 periods x 2 points = 4 total points (without insert_nulls)
    assert len(chart_data) == 4, "Should generate 4 points for 2 periods without insert_nulls"

    # No NULL separators
    assert all(point[1] is not None for point in chart_data)


def test_period_currency_conversion() -> None:
    """
    Test that period prices are correctly converted between major/subunit currency.

    Period prices are stored in major units (€/kr/$) in coordinator data.
    """
    period = {
        "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
        "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
        "price_median": 12.50,  # 12.50 €/kr (base currency)
    }

    # Test 1: Keep base currency (default for services)
    price_major = period["price_median"]
    assert price_major == 12.50, "Should keep major units (EUR)"

    # Test 2: Convert to subunit currency (if subunit_currency=True)
    price_minor = period["price_median"] * 100
    assert price_minor == 1250, "Should convert to minor units (ct/øre)"


def test_period_with_missing_end_time() -> None:
    """
    Test handling of periods without end time (incomplete period).

    If a period has no end time, we should only add the start point.
    """
    period = {
        "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
        "end": None,  # No end time
        "price_median": 12.50,
    }

    chart_data = []
    price_median = period["price_median"]
    start_serialized = period["start"].isoformat()
    end = period.get("end")
    end_serialized = end.isoformat() if end else None
    insert_nulls = "segments"

    # Add start point
    chart_data.append([start_serialized, price_median])

    # Only add end points if end_serialized exists
    if end_serialized:
        chart_data.append([end_serialized, price_median])
        if insert_nulls in ("segments", "all"):
            chart_data.append([end_serialized, None])

    # Verify: Only 1 point (start) for incomplete period
    assert len(chart_data) == 1, "Should only have start point for incomplete period"
    assert chart_data[0][1] == 12.50


def test_apexcharts_mapping_preserves_structure() -> None:
    """
    Test that ApexCharts .map() transformation preserves the 3-point structure.

    The ApexCharts data_generator uses: .map(point => [point[0], 1])
    This should preserve all 3 points but replace price with 1 (for overlay).
    """
    # Simulate period data (3 points per period with insert_nulls='segments')
    period_data = [
        ["2025-12-03T10:00:00+00:00", 1250],  # Start with price
        ["2025-12-03T12:00:00+00:00", 1250],  # End with price
        ["2025-12-03T12:00:00+00:00", None],  # End with NULL
    ]

    # Simulate ApexCharts mapping: [timestamp, 1] for overlay
    mapped_data = [[point[0], 1 if point[1] is not None else None] for point in period_data]

    # Verify structure is preserved
    assert len(mapped_data) == 3, "Should preserve all 3 points"
    assert mapped_data[0] == ["2025-12-03T10:00:00+00:00", 1]  # Start
    assert mapped_data[1] == ["2025-12-03T12:00:00+00:00", 1]  # End (hold)
    assert mapped_data[2] == ["2025-12-03T12:00:00+00:00", None]  # End (terminate)


def test_insert_nulls_all_mode() -> None:
    """
    Test that insert_nulls='all' also adds NULL terminators.

    The 'all' mode should behave the same as 'segments' for period data.
    """
    period = {
        "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
        "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
        "price_median": 1250,
    }

    chart_data = []
    price_median = period["price_median"]
    start_serialized = period["start"].isoformat()
    end_serialized = period["end"].isoformat()
    insert_nulls = "all"

    chart_data.append([start_serialized, price_median])
    chart_data.append([end_serialized, price_median])
    if insert_nulls in ("segments", "all"):
        chart_data.append([end_serialized, None])

    # Verify: 3 points with insert_nulls='all'
    assert len(chart_data) == 3, "Should generate 3 points with insert_nulls='all'"
    assert chart_data[2][1] is None


def test_insert_nulls_and_add_trailing_null_both_enabled() -> None:
    """
    Test that both insert_nulls and add_trailing_null work together correctly.

    When both are enabled, you should get:
    - NULL terminator after each period (from insert_nulls)
    - Additional NULL at the very end (from add_trailing_null)

    This results in TWO NULL points at the end: one for the last period, one trailing.
    """
    periods = [
        {
            "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
            "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
            "price_median": 1250,
        },
    ]

    chart_data = []
    insert_nulls = "segments"
    add_trailing_null = True

    for period in periods:
        price_median = period["price_median"]
        start_serialized = period["start"].isoformat()
        end_serialized = period["end"].isoformat()

        chart_data.append([start_serialized, price_median])
        chart_data.append([end_serialized, price_median])
        if insert_nulls in ("segments", "all"):
            chart_data.append([end_serialized, None])

    # Add trailing null
    if add_trailing_null:
        chart_data.append([None, None])

    # Verify: 3 points (period) + 1 trailing = 4 total
    assert len(chart_data) == 4, "Should have 4 points with both insert_nulls and add_trailing_null"

    # Last period's NULL terminator
    assert chart_data[2][0] == "2025-12-03T12:00:00+00:00"
    assert chart_data[2][1] is None

    # Trailing NULL (completely null)
    assert chart_data[3][0] is None
    assert chart_data[3][1] is None


def test_neither_insert_nulls_nor_add_trailing_null() -> None:
    """
    Test that when both insert_nulls='none' and add_trailing_null=False, no NULLs are added.

    This gives clean period data without any NULL separators.
    """
    period = {
        "start": datetime(2025, 12, 3, 10, 0, tzinfo=UTC),
        "end": datetime(2025, 12, 3, 12, 0, tzinfo=UTC),
        "price_median": 1250,
    }

    chart_data = []
    price_median = period["price_median"]
    start_serialized = period["start"].isoformat()
    end_serialized = period["end"].isoformat()
    insert_nulls = "none"
    add_trailing_null = False

    chart_data.append([start_serialized, price_median])
    chart_data.append([end_serialized, price_median])
    if insert_nulls in ("segments", "all"):
        chart_data.append([end_serialized, None])

    if add_trailing_null:
        chart_data.append([None, None])

    # Verify: Only 2 points (start, end) without any NULLs
    assert len(chart_data) == 2, "Should have 2 points without NULL insertion"
    assert all(point[1] is not None for point in chart_data), "No NULL values should be present"
