from __future__ import annotations

import json
import sys
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 ui.limits_dialog import LimitsDialog, _FallbackGtk
from routing_resolver import load_rules, update_account_cap

KNOWN_SLUGS = {"work", "zync2"}


def _snapshot(
    *,
    primary: int | None = 40,
    secondary: int | None = 88,
) -> AccountSnapshot:
    return AccountSnapshot(
        status=HealthStatus.OK,
        primary_used_pct=primary,
        secondary_used_pct=secondary,
        primary_reset_at=None,
        secondary_reset_at=None,
    )


def _write_rules(path: Path, payload: dict[str, object]) -> None:
    path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")


def _dialog(
    saved: list[tuple[str, int | None]],
    *,
    snapshot: AccountSnapshot = _snapshot(),
    caps: dict[str, int] = {},
    slug: str = "zync2",
) -> LimitsDialog:
    def on_save(window: str, cap_pct: int | None) -> None:
        saved.append((window, cap_pct))

    return LimitsDialog(
        slug,
        snapshot,
        caps,
        on_save,
        gtk_module=_FallbackGtk,
    )


def test_window_dropdown_derived_from_snapshot_grok_7d_only() -> None:
    dialog = _dialog([], snapshot=_snapshot(primary=None, secondary=50), slug="grok")
    assert dialog.window_combo._ids == [WINDOW_7D]
    assert dialog.window_combo.get_active_id() == WINDOW_7D


def test_window_dropdown_includes_5h_when_primary_present() -> None:
    dialog = _dialog([])
    assert dialog.window_combo._ids == [WINDOW_5H, WINDOW_7D]


def test_prefills_existing_cap_and_shows_remaining() -> None:
    dialog = _dialog([], caps={WINDOW_7D: 10})
    dialog.window_combo.set_active_id(WINDOW_7D)
    dialog._sync_for_window(WINDOW_7D)
    assert dialog.cap_entry.get_text() == "10"
    assert dialog.remaining_label.text == "now: 12% remaining"


def test_save_persists_cap_round_trip(tmp_path: Path) -> None:
    path = tmp_path / "routing_rules.json"
    _write_rules(
        path,
        {
            "version": "routing/v2",
            "projects": {},
            "default": "work",
            "fallback_chain": [],
            "quota_exhausted_threshold_pct": 100,
            "account_caps": {},
        },
    )
    saved: list[tuple[str, int | None]] = []
    dialog = _dialog(saved)
    dialog.simulate_save(WINDOW_7D, "10")
    assert saved == [(WINDOW_7D, 10)]
    update_account_cap(
        path,
        "zync2",
        WINDOW_7D,
        10,
        known_slugs=KNOWN_SLUGS,
    )
    rules = load_rules(path, known_slugs=KNOWN_SLUGS)
    assert dict(rules.account_caps) == {"zync2": {WINDOW_7D: 10}}


def test_blank_save_clears_cap(tmp_path: Path) -> None:
    path = tmp_path / "routing_rules.json"
    _write_rules(
        path,
        {
            "version": "routing/v2",
            "projects": {},
            "default": "work",
            "fallback_chain": [],
            "quota_exhausted_threshold_pct": 100,
            "account_caps": {"zync2": {WINDOW_7D: 10}},
        },
    )
    saved: list[tuple[str, int | None]] = []
    dialog = _dialog(saved, caps={WINDOW_7D: 10})
    dialog.simulate_save(WINDOW_7D, "")
    assert saved == [(WINDOW_7D, None)]
    update_account_cap(
        path,
        "zync2",
        WINDOW_7D,
        None,
        known_slugs=KNOWN_SLUGS,
    )
    rules = load_rules(path, known_slugs=KNOWN_SLUGS)
    assert dict(rules.account_caps) == {}


def test_cap_entry_clamps_on_read() -> None:
    saved: list[tuple[str, int | None]] = []
    dialog = _dialog(saved)
    dialog.simulate_save(WINDOW_7D, "150")
    assert saved == [(WINDOW_7D, 100)]


def test_cancel_does_not_call_on_save() -> None:
    saved: list[tuple[str, int | None]] = []
    dialog = _dialog(saved)
    dialog.open()
    assert saved == []
    assert dialog.saved is None
    assert dialog.dialog.destroyed is True


def test_label_reads_stop_when_remaining_falls_below() -> None:
    dialog = _dialog([])
    labels = [
        child.text
        for child in dialog.dialog._content.children
        if hasattr(child, "children")
        for child in child.children
        if getattr(child, "text", "").startswith("Stop when remaining falls below:")
    ]
    assert labels == ["Stop when remaining falls below:"]


def test_update_account_cap_preserves_other_fields(tmp_path: Path) -> None:
    path = tmp_path / "routing_rules.json"
    _write_rules(
        path,
        {
            "version": "routing/v2",
            "projects": {"proj": {"account": "work"}},
            "default": "work",
            "fallback_chain": ["zync2"],
            "fallback_trigger": "broken_or_quota_exhausted",
            "missing_health_is_available": False,
            "quota_exhausted_threshold_pct": 90,
            "account_caps": {},
        },
    )
    update_account_cap(
        path,
        "zync2",
        WINDOW_7D,
        25,
        known_slugs=KNOWN_SLUGS,
    )
    payload = json.loads(path.read_text(encoding="utf-8"))
    assert payload["projects"] == {"proj": {"account": "work"}}
    assert payload["default"] == "work"
    assert payload["fallback_chain"] == ["zync2"]
    assert payload["quota_exhausted_threshold_pct"] == 90
    assert payload["account_caps"] == {"zync2": {WINDOW_7D: 25}}


def test_update_account_cap_rejects_invalid_cap(tmp_path: Path) -> None:
    path = tmp_path / "routing_rules.json"
    _write_rules(
        path,
        {
            "version": "routing/v2",
            "projects": {},
            "default": "work",
            "fallback_chain": [],
            "quota_exhausted_threshold_pct": 100,
            "account_caps": {},
        },
    )
    with pytest.raises(ValueError, match="1 to 100"):
        update_account_cap(
            path,
            "zync2",
            WINDOW_7D,
            0,
            known_slugs=KNOWN_SLUGS,
        )


def test_save_prunes_caps_for_slugs_no_longer_in_the_registry(tmp_path: Path) -> None:
    path = tmp_path / "routing_rules.json"
    _write_rules(
        path,
        {
            "version": "routing/v2",
            "projects": {},
            "default": "work",
            "fallback_chain": [],
            "quota_exhausted_threshold_pct": 100,
            "account_caps": {"renamed-away": {WINDOW_7D: 40}, "work": {WINDOW_5H: 20}},
        },
    )

    update_account_cap(path, "zync2", WINDOW_7D, 10, known_slugs=KNOWN_SLUGS)

    rules = load_rules(path, known_slugs=KNOWN_SLUGS)
    assert dict(rules.account_caps) == {
        "work": {WINDOW_5H: 20},
        "zync2": {WINDOW_7D: 10},
    }
