from __future__ import annotations

import re
import sys
from datetime import datetime
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from health_client import AccountSnapshot, HealthStatus
from limit_warning import WINDOW_5H, WINDOW_7D
from spend_cap import cap_reset_at, capped_windows, describe_caps


def _parse_reset_at(value: str) -> float | None:
    match = re.fullmatch(
        r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-]\d{2}:\d{2})",
        value,
    )
    if not match:
        return None
    year, month, day, hour, minute, second = (int(part) for part in match.groups()[:6])
    max_day = [
        31,
        29 if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) else 28,
        31,
        30,
        31,
        30,
        31,
        31,
        30,
        31,
        30,
        31,
    ][month - 1]
    offset = None if match.group(7) == "Z" else [int(x) for x in match.group(7)[1:].split(":")]
    if (
        month < 1
        or month > 12
        or day < 1
        or day > max_day
        or hour > 23
        or minute > 59
        or second > 59
        or (offset and (offset[0] > 23 or offset[1] > 59))
    ):
        return None
    parsed = datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp()
    return parsed if parsed == parsed else None


@pytest.mark.parametrize(
    ("used_pct", "cap_pct", "expected"),
    [
        (90, 10, True),
        (89, 10, False),
        (100, 1, True),
        (None, 10, False),
    ],
)
def test_capped_windows_remaining_boundary_table(used_pct: int | None, cap_pct: int, expected: bool) -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, None, used_pct)
    result = capped_windows(snapshot, {WINDOW_7D: cap_pct})
    if expected:
        assert result == frozenset({WINDOW_7D})
    else:
        assert result == frozenset()


def test_capped_windows_no_cap_for_window() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, 95, 95)
    assert capped_windows(snapshot, {}) == frozenset()


def test_capped_windows_both_windows_capped() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, 96, 98)
    assert capped_windows(snapshot, {WINDOW_5H: 5, WINDOW_7D: 3}) == frozenset({WINDOW_5H, WINDOW_7D})


def test_capped_windows_remaining_equals_cap_is_capped() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, None, 90)
    assert capped_windows(snapshot, {WINDOW_7D: 10}) == frozenset({WINDOW_7D})


def test_capped_windows_remaining_equals_cap_plus_one_is_clear() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, None, 89)
    assert capped_windows(snapshot, {WINDOW_7D: 10}) == frozenset()


def test_describe_caps_orders_5h_before_7d() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, 96, 98)
    assert describe_caps(snapshot, {WINDOW_5H: 5, WINDOW_7D: 3}) == "capped(5h=4% left <= 5%, 7d=2% left <= 3%)"


def test_describe_caps_empty_when_clear() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, 10, 20)
    assert describe_caps(snapshot, {WINDOW_7D: 10}) == ""


def test_cap_reset_at_truncates_epoch_seconds_without_rounding_up() -> None:
    snapshot = AccountSnapshot(
        HealthStatus.OK,
        None,
        88,
        secondary_reset_at=1786365653.9,
    )
    result = cap_reset_at(snapshot, [WINDOW_7D])
    assert result == "2026-08-10T12:40:53Z"
    assert _parse_reset_at(result) is not None


def test_cap_reset_at_returns_earliest_reset_across_windows() -> None:
    snapshot = AccountSnapshot(
        HealthStatus.OK,
        50,
        50,
        primary_reset_at=2000.0,
        secondary_reset_at=1000.0,
    )
    result = cap_reset_at(snapshot, [WINDOW_5H, WINDOW_7D])
    assert result == "1970-01-01T00:16:40Z"
    assert _parse_reset_at(result) is not None


def test_cap_reset_at_returns_none_when_no_reset_known() -> None:
    snapshot = AccountSnapshot(HealthStatus.OK, 50, 50)
    assert cap_reset_at(snapshot, [WINDOW_5H, WINDOW_7D]) is None


def test_zync2_remaining_cap_inversion_regression() -> None:
    caps = {WINDOW_7D: 10}
    not_capped = AccountSnapshot(HealthStatus.OK, None, 88, secondary_reset_at=1786365653.0)
    capped = AccountSnapshot(HealthStatus.OK, None, 90, secondary_reset_at=1786365653.0)
    healthy = AccountSnapshot(HealthStatus.OK, None, 10, secondary_reset_at=1786365653.0)
    assert capped_windows(not_capped, caps) == frozenset()
    assert capped_windows(capped, caps) == frozenset({WINDOW_7D})
    assert capped_windows(healthy, caps) == frozenset()
