"""Session weights: what this conversation has been editing and reading."""

from __future__ import annotations

import math
import os
import time
from datetime import datetime, timezone
from pathlib import Path

from . import store

SCHEMA_VERSION = 1
MAX_ENTRIES = 4000
HALF_LIFE_SECONDS = 6 * 3600.0
KIND_WEIGHT = {"edit": 1.0, "read": 0.35}


def _agent_id(explicit: object = None) -> str | None:
    if isinstance(explicit, str) and explicit.strip():
        return explicit.strip()
    value = os.environ.get("ZYTE_AGENT_ID") or os.environ.get("ZYTE_AGENT_SESSION_ID")
    return value or None


def load(root: Path) -> list[dict]:
    data = store.read_json(store.action_graph_path(root))
    if data is None or data.get("schema_version") != SCHEMA_VERSION:
        return []
    entries = data.get("entries")
    if not isinstance(entries, list):
        return []
    return [entry for entry in entries if isinstance(entry, dict) and isinstance(entry.get("path"), str)]


def register(root: Path, events: list[tuple[str, str | None, str | None]], kind: str) -> int:
    """Merge one batched turn into the action graph. ``events`` are (path, symbol, agent_id)."""
    unique: dict[tuple[str, str | None], str | None] = {}
    for path, symbol, agent in events:
        if not path:
            continue
        key = (path, symbol)
        if key not in unique:
            unique[key] = agent
    if not unique:
        return 0

    now = time.time()
    entries = load(root)
    index = {(entry["path"], entry.get("symbol"), entry.get("kind")): entry for entry in entries}
    for (path, symbol), agent in unique.items():
        existing = index.get((path, symbol, kind))
        if existing is None:
            existing = {"path": path, "symbol": symbol, "kind": kind, "count": 0, "last_ts": now}
            entries.append(existing)
            index[(path, symbol, kind)] = existing
        existing["count"] = int(existing.get("count") or 0) + 1
        existing["last_ts"] = now
        if agent:
            existing["agent_id"] = agent

    entries.sort(key=lambda entry: float(entry.get("last_ts") or 0.0), reverse=True)
    del entries[MAX_ENTRIES:]
    updated_at = datetime.fromtimestamp(now, timezone.utc).isoformat().replace("+00:00", "Z")
    store.write_json(
        store.action_graph_path(root),
        {"schema_version": SCHEMA_VERSION, "updated_at": updated_at, "entries": entries},
    )
    return len(unique)


def scores(root: Path, now: float | None = None) -> dict[tuple[str, str | None], float]:
    """Decayed weight per (path, symbol); symbol None means whole-file activity."""
    moment = time.time() if now is None else now
    out: dict[tuple[str, str | None], float] = {}
    for entry in load(root):
        try:
            count = int(entry.get("count") or 0)
            last = float(entry.get("last_ts") or 0.0)
        except (TypeError, ValueError):
            continue
        if count <= 0:
            continue
        base = KIND_WEIGHT.get(str(entry.get("kind")), 0.2)
        age = max(0.0, moment - last)
        decay = 0.5 ** (age / HALF_LIFE_SECONDS)
        value = base * decay * math.log1p(count)
        key = (entry["path"], entry.get("symbol") if isinstance(entry.get("symbol"), str) else None)
        out[key] = out.get(key, 0.0) + value
    return out
