"""Simple local-only session tracking.

NO phone-home, NO external APIs.  All data stays in ``~/.tribunal/sessions/``.

Each session is stored as a JSON file named by its session ID (a UUID).
"""

from __future__ import annotations

import json
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from launcher.config import get_sessions_dir


def _now_iso() -> str:
    """Return the current UTC time as an ISO-8601 string."""
    return datetime.now(timezone.utc).isoformat()


def _session_path(session_id: str) -> Path:
    """Return the file path for a given session ID."""
    return get_sessions_dir() / f"{session_id}.json"


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def start_session(metadata: dict[str, Any] | None = None) -> str:
    """Create a new session and return its ID.

    Parameters
    ----------
    metadata:
        Optional extra key-value pairs to store with the session.
    """
    session_id = uuid.uuid4().hex
    record: dict[str, Any] = {
        "id": session_id,
        "started_at": _now_iso(),
        "ended_at": None,
        "duration_seconds": None,
    }
    if metadata:
        record["metadata"] = metadata

    path = _session_path(session_id)
    try:
        with open(path, "w", encoding="utf-8") as fh:
            json.dump(record, fh, indent=2)
            fh.write("\n")
    except OSError:
        pass

    return session_id


def end_session(session_id: str) -> dict[str, Any] | None:
    """Mark a session as ended and record its duration.

    Returns the updated session record, or ``None`` if the session file was
    not found.
    """
    path = _session_path(session_id)
    if not path.is_file():
        return None

    try:
        with open(path, "r", encoding="utf-8") as fh:
            record: dict[str, Any] = json.load(fh)
    except (json.JSONDecodeError, OSError):
        return None

    end_time = _now_iso()
    record["ended_at"] = end_time

    # Compute duration.
    try:
        started = datetime.fromisoformat(record["started_at"])
        ended = datetime.fromisoformat(end_time)
        record["duration_seconds"] = round((ended - started).total_seconds(), 2)
    except (ValueError, KeyError):
        record["duration_seconds"] = None

    try:
        with open(path, "w", encoding="utf-8") as fh:
            json.dump(record, fh, indent=2)
            fh.write("\n")
    except OSError:
        pass

    return record


def get_session(session_id: str) -> dict[str, Any] | None:
    """Load and return a session record by ID, or ``None`` if not found."""
    path = _session_path(session_id)
    if not path.is_file():
        return None
    try:
        with open(path, "r", encoding="utf-8") as fh:
            return json.load(fh)
    except (json.JSONDecodeError, OSError):
        return None


def list_sessions(limit: int = 20) -> list[dict[str, Any]]:
    """Return the most recent sessions, newest first.

    Parameters
    ----------
    limit:
        Maximum number of sessions to return.
    """
    sessions_dir = get_sessions_dir()
    files = sorted(sessions_dir.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True)
    results: list[dict[str, Any]] = []
    for f in files[:limit]:
        try:
            with open(f, "r", encoding="utf-8") as fh:
                results.append(json.load(fh))
        except (json.JSONDecodeError, OSError):
            continue
    return results


def active_session() -> dict[str, Any] | None:
    """Return the most recent session that has not been ended, if any."""
    for session in list_sessions(limit=50):
        if session.get("ended_at") is None:
            return session
    return None
