"""Menschliches Session-Verhalten: Spielbloecke, Pausen, Tageslimit.

Kein Mensch spielt stundenlang ohne Unterbrechung im gleichen Takt.
Dieser Manager erzwingt zufaellige Pausen (Mikro-AFK, laengere Bloecke,
Tageslimit) ueber den vorhandenen ng_pause-Mechanismus inkl. Keep-Alive.

Config per Env:
  TIBIAPILOT_SESSION=0                    -> komplett aus
  TIBIAPILOT_SESSION_DAILY_MAX_MINUTES    -> Tagesbudget (Default 480)
"""
import json
import os
import random
import time
from pathlib import Path

from src.gameplay.monitoring import logEvent

SESSION_ENV = 'TIBIAPILOT_SESSION'
DAILY_MAX_ENV = 'TIBIAPILOT_SESSION_DAILY_MAX_MINUTES'
STATE_FILE_ENV = 'TIBIAPILOT_SESSION_FILE'
DEFAULT_STATE_FILE = '.pilotng-session.json'


def _gauss(mu, sigma, lo, hi):
    return min(max(random.gauss(mu, sigma), lo), hi)


def _restOfDay(now):
    """Sekunden bis morgen ~06:00 Uhr (+ bis zu 60 min Jitter)."""
    tomorrow = time.localtime(now + 24 * 3600)
    target = time.mktime((
        tomorrow.tm_year, tomorrow.tm_mon, tomorrow.tm_mday,
        6, 0, 0, 0, 0, -1,
    ))
    return max(target - now, 3600) + _gauss(0, 20 * 60, 0, 60 * 60)


class SessionManager:
    def __init__(self):
        self.enabled = os.getenv(SESSION_ENV, '1') != '0'
        self.dailyMaxSeconds = float(os.getenv(DAILY_MAX_ENV, '480')) * 60
        self.stateFile = Path(os.getenv(STATE_FILE_ENV, DEFAULT_STATE_FILE))
        self.state = self._load()
        self.lastTickAt = None
        self.lastSaveAt = 0.0
        self.userOverride = False

    # --- Persistenz ---------------------------------------------------------
    def _load(self):
        try:
            return json.loads(self.stateFile.read_text(encoding='utf-8'))
        except (OSError, ValueError):
            return None

    def _save(self, now):
        self.lastSaveAt = now
        try:
            self.stateFile.write_text(
                json.dumps(self.state), encoding='utf-8',
            )
        except OSError:
            pass

    # --- Planung ------------------------------------------------------------
    def _planBlock(self, now):
        self.state['blockEndsAt'] = now + _gauss(210, 45, 120, 330) * 60
        self.state['afkAt'] = now + _gauss(40, 10, 25, 70) * 60

    def _ensureToday(self, now):
        today = time.strftime('%Y-%m-%d', time.localtime(now))
        if not self.state or self.state.get('day') != today:
            self.state = {
                'day': today,
                'playedSeconds': 0.0,
                'breakUntil': 0.0,
            }
            self._planBlock(now)
            self._save(now)
        return self.state

    def _startBreak(self, context, now, duration):
        self.state['breakUntil'] = now + duration
        self.userOverride = False
        context['ng_pause'] = True
        logEvent('sessionBreak', {'minutes': round(duration / 60, 1)})
        self._save(now)

    # --- Haupteinstieg (jede Loop-Iteration) ---------------------------------
    def tick(self, context, now=None):
        if not self.enabled:
            return context
        now = time.time() if now is None else now
        state = self._ensureToday(now)

        if now < state['breakUntil']:
            if not context['ng_pause']:
                # Spieler hat manuell weitergemacht: Wunsch respektieren.
                self.userOverride = True
            elif not self.userOverride:
                context['ng_pause'] = True
            context['ng_sessionBreak'] = True
            return context

        context['ng_sessionBreak'] = False
        if state['breakUntil']:
            state['breakUntil'] = 0.0
            self.userOverride = False
            if context['ng_pause']:
                # Nur die eigene Zwangspause wieder aufheben.
                context['ng_pause'] = False
            logEvent('sessionResume')
            self._planBlock(now)
            self._save(now)

        if context['ng_pause']:
            # Externe Pause (UI/Alarm) zaehlt nicht als Spielzeit.
            self.lastTickAt = now
            return context

        if self.lastTickAt is not None:
            state['playedSeconds'] += min(now - self.lastTickAt, 5)
        self.lastTickAt = now
        if now - self.lastSaveAt >= 60:
            self._save(now)

        if state['playedSeconds'] >= self.dailyMaxSeconds:
            self._startBreak(context, now, _restOfDay(now))
        elif now >= state['blockEndsAt']:
            self._startBreak(context, now, _gauss(30, 10, 15, 60) * 60)
        elif now >= state['afkAt']:
            self._startBreak(context, now, _gauss(2.5, 1, 1, 5) * 60)
        return context
