import json
import time

import pytest

from src.gameplay import session as session_module
from src.gameplay.session import SessionManager

NOW = 1_800_000_000  # fixer, gueltiger Zeitpunkt


@pytest.fixture
def manager(tmp_path, monkeypatch):
    monkeypatch.setenv(
        session_module.STATE_FILE_ENV, str(tmp_path / 'session.json'),
    )
    monkeypatch.setenv(
        'TIBIAPILOT_EVENTS_FILE', str(tmp_path / 'events.jsonl'),
    )
    monkeypatch.delenv(session_module.SESSION_ENV, raising=False)
    monkeypatch.delenv(session_module.DAILY_MAX_ENV, raising=False)
    # Zufall in Tests fixieren: gauss liefert immer den Mittelwert.
    monkeypatch.setattr(
        session_module.random, 'gauss', lambda mu, sigma: mu,
    )
    return SessionManager()


def ctx(paused=False):
    return {'ng_pause': paused}


def test_disabled_passthrough(tmp_path, monkeypatch):
    monkeypatch.setenv(session_module.SESSION_ENV, '0')
    monkeypatch.setenv(
        session_module.STATE_FILE_ENV, str(tmp_path / 'session.json'),
    )
    manager = SessionManager()
    context = ctx()
    assert manager.tick(context, NOW) is context
    assert context['ng_pause'] is False


def test_first_tick_plans_block_and_persists(manager, tmp_path):
    manager.tick(ctx(), NOW)
    state = json.loads((tmp_path / 'session.json').read_text())
    assert state['blockEndsAt'] == NOW + 210 * 60
    assert state['afkAt'] == NOW + 40 * 60
    assert state['playedSeconds'] == 0.0


def test_playtime_accumulates_with_cap(manager):
    manager.tick(ctx(), NOW)
    manager.tick(ctx(), NOW + 4)
    assert manager.state['playedSeconds'] == 4
    # Lange Luecken werden auf 5 s gedeckelt (kein Zeit-Sprung-Betrug).
    manager.tick(ctx(), NOW + 4000)
    assert manager.state['playedSeconds'] == 9


def test_block_end_starts_break(manager):
    manager.tick(ctx(), NOW)
    context = manager.tick(ctx(), NOW + 211 * 60)
    assert context['ng_pause'] is True
    assert manager.state['breakUntil'] == NOW + 211 * 60 + 30 * 60


def test_break_holds_and_releases_pause(manager):
    manager.tick(ctx(), NOW)
    manager.tick(ctx(), NOW + 211 * 60)
    during = manager.tick(ctx(True), NOW + 212 * 60)
    assert during['ng_pause'] is True
    assert during['ng_sessionBreak'] is True
    after = manager.tick(ctx(True), NOW + 242 * 60)
    assert after['ng_pause'] is False
    assert after['ng_sessionBreak'] is False
    assert manager.state['breakUntil'] == 0.0


def test_user_override_during_break_is_respected(manager):
    manager.tick(ctx(), NOW)
    manager.tick(ctx(), NOW + 211 * 60)
    # Spieler drueckt waehrend der Zwangspause selbst auf Play.
    context = manager.tick(ctx(False), NOW + 212 * 60)
    assert context['ng_pause'] is False


def test_micro_afk_triggers_short_break(manager):
    manager.tick(ctx(), NOW)
    context = manager.tick(ctx(), NOW + 41 * 60)
    assert context['ng_pause'] is True
    assert manager.state['breakUntil'] == NOW + 41 * 60 + 2.5 * 60


def test_daily_cap_breaks_until_next_morning(tmp_path, monkeypatch):
    monkeypatch.setenv(
        session_module.STATE_FILE_ENV, str(tmp_path / 'session.json'),
    )
    monkeypatch.delenv(session_module.SESSION_ENV, raising=False)
    monkeypatch.setenv(session_module.DAILY_MAX_ENV, '1')
    monkeypatch.setattr(
        session_module.random, 'gauss', lambda mu, sigma: mu,
    )
    manager = SessionManager()
    context = ctx()
    for index in range(15):
        context = manager.tick(context, NOW + index * 5)
    assert context['ng_pause'] is True
    assert manager.state['breakUntil'] - (NOW + 14 * 5) >= 3600


def test_day_rollover_resets_budget(manager):
    manager.tick(ctx(), NOW)
    manager.state['playedSeconds'] = 99999
    manager.tick(ctx(), NOW + 2 * 24 * 3600)
    assert manager.state['playedSeconds'] < 99999
    assert manager.state['day'] != time.strftime(
        '%Y-%m-%d', time.localtime(NOW),
    )
