from pathlib import Path

from hockey_data.service import HockeyDataService

ROOT = Path(__file__).resolve().parents[1]
APP_JS = (ROOT / "ui_builder/static/app.js").read_text(encoding="utf-8")
APP = (ROOT / "app.py").read_text(encoding="utf-8")


def _penalty(side: str, remaining: int, *, started: bool) -> dict:
    return {
        "side": side,
        "infraction": {"id": "minor"},
        "preset": "2m",
        "durationMs": 120000,
        "remainingMs": remaining,
        "running": False,
        "startedOnce": started,
        "finished": False,
    }


def _strength(*penalties: dict) -> dict:
    settings = {
        "strength_regulation_skaters": 5,
        "strength_min_skaters": 3,
        "strength_state_labels": {
            "regulation": {
                "5x4": {"ru": "БОЛ"},
                "4x4": {"ru": "4x4"},
                "4x3": {"ru": "4x3"},
            }
        },
    }
    return HockeyDataService._strength_payload(
        settings,
        stage="regular",
        current_period="2",
        timer_state={"penalty_board": {"penalties": list(penalties)}},
        language="ru",
    )


def test_prepared_penalty_does_not_change_strength_until_explicit_start():
    prepared = _strength(_penalty("home", 120000, started=False))
    assert prepared["strength_label"] == "5×5"
    assert prepared["advantage_side"] == ""

    started = _strength(_penalty("home", 120000, started=True))
    assert started["strength_label"] == "4×5"
    assert started["advantage_side"] == "away"
    assert started["state_label"] == "БОЛ"


def test_complex_transition_strength_sequence_matches_requested_flow():
    # Initial HOME penalty: AWAY/right has the advantage.
    p1 = _penalty("home", 80000, started=True)
    value = _strength(p1)
    assert value["strength_label"] == "4×5"
    assert value["advantage_side"] == "away"

    # Coincidental pair is added while the first penalty is still running.
    p2 = _penalty("home", 120000, started=True)
    p3 = _penalty("away", 120000, started=True)
    value = _strength(p1, p2, p3)
    assert value["strength_label"] == "3×4"
    assert value["advantage_side"] == "away"
    assert value["state_label"] == "4x3"

    # The first penalty expires: the pair remains, so strength is equal 4x4.
    value = _strength(
        _penalty("home", 40000, started=True),
        _penalty("away", 40000, started=True),
    )
    assert value["strength_label"] == "4×4"
    assert value["advantage_side"] == ""
    assert value["state_label"] == "4x4"

    # AWAY/right takes another penalty: HOME/left now has a 4x3 advantage.
    value = _strength(
        _penalty("home", 30000, started=True),
        _penalty("away", 30000, started=True),
        _penalty("away", 120000, started=True),
    )
    assert value["strength_label"] == "4×3"
    assert value["advantage_side"] == "home"
    assert value["state_label"] == "4x3"

    # Coincidental pair expires, leaving ordinary HOME power play.
    value = _strength(_penalty("away", 90000, started=True))
    assert value["strength_label"] == "5×4"
    assert value["advantage_side"] == "home"
    assert value["state_label"] == "БОЛ"


def test_runtime_holds_single_plate_through_temporary_equal_strength():
    start = APP_JS.index("function penaltyDisplayEntriesByTargetSide")
    end = APP_JS.index("function hockeyPenaltySideMappingDetail", start)
    block = APP_JS[start:end]
    assert "canHoldEqualStrength" in block
    assert "state.hockeyPenaltyAdvantageCycle.hadAdvantage" in block
    assert "lastAdvantageSide" in block
    assert "const canHoldEqualStrength = Boolean(state.hockeyPenaltyAdvantageCycle.hadAdvantage)" in block
    assert 'home: rememberedSide === "home" && transitionEntry ? [transitionEntry] : []' in block
    assert 'away: rememberedSide === "away" && transitionEntry ? [transitionEntry] : []' in block
    assert 'home: advantageSide === "home" && transitionEntry ? [transitionEntry] : []' in block
    assert 'away: advantageSide === "away" && transitionEntry ? [transitionEntry] : []' in block
    assert "holdingEqualStrength" in block
    # The countdown source remains the globally soonest active penalty.
    assert "const transitionEntry = all[0] || null;" in block


def test_pure_equal_strength_without_previous_advantage_still_has_no_plate():
    start = APP_JS.index("function penaltyDisplayEntriesByTargetSide")
    end = APP_JS.index("function hockeyPenaltySideMappingDetail", start)
    block = APP_JS[start:end]
    assert "if (!canHoldEqualStrength)" in block
    assert "return { home: [], away: [], routedToAdvantage: false" in block


def test_advantage_cycle_is_saved_and_restored_for_reload_during_4x4():
    snapshot = APP_JS.split("function hockeyGameTimerSnapshot", 1)[1].split("function hockeyApplySavedTimers", 1)[0]
    assert "advantage_cycle" in snapshot
    assert "had_advantage" in snapshot
    assert "last_advantage_side" in snapshot
    restore = APP_JS.split("function hockeyApplySavedTimers", 1)[1].split("async function hockeyPersistGameTimers", 1)[0]
    assert "restoredPenaltyAdvantageCycle" in restore
    assert "savedCycle.last_advantage_side" in restore

    normalized = HockeyDataService._normalise_game_timer_state({
        "penalty_board": {
            "penalties": [],
            "history": [],
            "advantage_cycle": {"had_advantage": True, "last_advantage_side": "away"},
        }
    })
    assert normalized["penalty_board"]["advantage_cycle"] == {
        "had_advantage": True,
        "last_advantage_side": "away",
    }


def test_full_strength_waits_for_last_started_penalty_not_prepared_draft():
    control = APP_JS.split("function controlHockeyPenalty", 1)[1].split("function formatHockeyPenaltyTime", 1)[0]
    assert "hockeyPenaltyHasStarted(item)" in control
    ticker = APP_JS.split("function updateHockeyPenaltyBoards", 1)[1].split("function registerHockeyBoardNode", 1)[0]
    assert "remainingTotal = board.penalties.filter((item) => !item.finished && hockeyEventReady(item) && hockeyPenaltyHasStarted(item)).length" in ticker


def test_build96_runtime_version():
    assert 'BUILD_VERSION = "2026.08.20.17"' in APP
