"""
Evolution metadata: read/write evolution_meta.json and per-model adapter lookup.
"""
from __future__ import annotations

import json
import os
import re
import tempfile

_EVOLUTION_DIR = os.path.join(os.path.expanduser("~"), ".mugi", "evolution")
_META_PATH = os.path.join(_EVOLUTION_DIR, "evolution_meta.json")
_DIR_MODE = 0o700

MLX_ADAPTERS_DIR = os.path.join(os.path.expanduser("~"), ".mugi", "mlx", "adapters")

_DEFAULT_META = {
    "last_training_at": None,
    "last_trajectory_count": 0,
    "adapters_by_model": {},
}


def _log_evolution(msg: str, level: str = "WARNING") -> None:
    try:
        from tools._logging import _debug_log
        _debug_log(f"[evolution/meta] {msg}", level)
    except Exception:
        pass


def _ensure_dir():
    os.makedirs(_EVOLUTION_DIR, mode=_DIR_MODE, exist_ok=True)


def read_evolution_meta() -> dict:
    """Read evolution_meta.json; return defaults if missing or corrupt."""
    try:
        if os.path.isfile(_META_PATH):
            with open(_META_PATH, "r", encoding="utf-8") as f:
                data = json.load(f)
            if isinstance(data, dict):
                for k, v in _DEFAULT_META.items():
                    data.setdefault(k, v)
                return data
    except Exception as e:
        _log_evolution(f"read_evolution_meta failed: {e}", "ERROR")
    return dict(_DEFAULT_META)


def write_evolution_meta(meta: dict) -> None:
    """Write evolution_meta.json atomically (write to tmp, then rename)."""
    _ensure_dir()
    fd, tmp_path = tempfile.mkstemp(dir=_EVOLUTION_DIR, suffix=".tmp")
    try:
        with os.fdopen(fd, "w") as f:
            json.dump(meta, f, indent=2)
        os.replace(tmp_path, _META_PATH)
    except Exception:
        try:
            os.unlink(tmp_path)
        except OSError:
            pass
        raise


def model_slug(model_path: str) -> str:
    """Derive a filesystem-safe slug from a model path/repo.

    e.g. 'mlx-community/Qwen3-8B-4bit' -> 'Qwen3-8B-4bit'
    """
    name = model_path.strip().rstrip("/")
    if "/" in name:
        name = name.rsplit("/", 1)[-1]
    name = re.sub(r"[^\w\-.]", "_", name)
    return name or "unknown_model"


def _is_safe_adapter_path(adapter_name: str) -> bool:
    """Verify that the adapter name resolves to a path inside MLX_ADAPTERS_DIR.

    Prevents path traversal attacks via tampered evolution_meta.json.
    """
    if not adapter_name:
        return False
    if "/" in adapter_name or "\\" in adapter_name or ".." in adapter_name:
        return False
    candidate = os.path.realpath(os.path.join(MLX_ADAPTERS_DIR, adapter_name))
    parent = os.path.realpath(MLX_ADAPTERS_DIR)
    return candidate.startswith(parent + os.sep) or candidate == parent


def get_evolution_adapter_for_model(model_path: str) -> str | None:
    """Return the evolution adapter name for the given model, or None.

    Only returns a name if the adapter directory actually exists on disk
    and resolves to a path inside MLX_ADAPTERS_DIR (preventing traversal).
    """
    if not model_path:
        return None
    meta = read_evolution_meta()
    adapter_name = meta.get("adapters_by_model", {}).get(model_path)
    if not adapter_name:
        return None
    if not _is_safe_adapter_path(adapter_name):
        _log_evolution(
            f"Rejected adapter name with suspicious path: {adapter_name!r}",
            "ERROR",
        )
        return None
    adapter_dir = os.path.join(MLX_ADAPTERS_DIR, adapter_name)
    if os.path.isdir(adapter_dir):
        return adapter_name
    return None
