"""Manage the ``~/.tribunal/`` configuration directory.

Directory layout::

    ~/.tribunal/
        config.json      -- User settings
        sessions/        -- Session tracking data
        memory/          -- Persistent memory store
        bin/             -- Launcher helper scripts
"""

from __future__ import annotations

import json
import os
import re
from pathlib import Path
from typing import Any

_SAFE_SESSION_ID = re.compile(r"[^A-Za-z0-9_\-]")


def sanitize_session_id(session_id: str) -> str:
    """Return a filesystem-safe session ID with path-traversal chars removed.

    Only alphanumeric characters, hyphens, and underscores are kept.
    Falls back to ``"default"`` if the result would be empty.
    """
    safe = _SAFE_SESSION_ID.sub("", session_id)
    return safe or "default"


_DEFAULT_HOME: Path = Path.home() / ".tribunal"


def get_tribunal_home() -> Path:
    """Return the Tribunal home directory, creating it if needed."""
    home = Path(os.environ.get("SKILLFIELD_HOME", str(_DEFAULT_HOME)))
    home.mkdir(parents=True, exist_ok=True)
    return home


def get_config_path() -> Path:
    """Return the path to ``config.json``."""
    return get_tribunal_home() / "config.json"


def get_sessions_dir() -> Path:
    """Return the ``sessions/`` directory, creating it if needed."""
    d = get_tribunal_home() / "sessions"
    d.mkdir(parents=True, exist_ok=True)
    return d


def get_memory_dir() -> Path:
    """Return the ``memory/`` directory, creating it if needed."""
    d = get_tribunal_home() / "memory"
    d.mkdir(parents=True, exist_ok=True)
    return d


_DEFAULT_CONFIG: dict[str, Any] = {
    "env": {},
    "default_args": [],
    "mode": "quick",
    "auto_update": True,
    "model": None,
    "custom_models": [],
    "providers": {},
}


def get_config() -> dict[str, Any]:
    """Load and return the config dict from ``config.json``.

    If the file does not exist or is invalid JSON, returns a copy of the
    default configuration and writes it to disk.
    """
    path = get_config_path()
    if path.is_file():
        try:
            with open(path, "r", encoding="utf-8") as fh:
                data: dict[str, Any] = json.load(fh)
            for key, value in _DEFAULT_CONFIG.items():
                data.setdefault(key, value)
            return data
        except (json.JSONDecodeError, OSError):
            pass

    save_config(_DEFAULT_CONFIG)
    return dict(_DEFAULT_CONFIG)


def save_config(config: dict[str, Any]) -> None:
    """Persist *config* to ``config.json``."""
    path = get_config_path()
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w", encoding="utf-8") as fh:
        json.dump(config, fh, indent=2, sort_keys=True)
        fh.write("\n")
    os.chmod(path, 0o600)



# ---------------------------------------------------------------------------
# Model configuration helpers
# ---------------------------------------------------------------------------


def get_model() -> str | None:
    """Return the configured default model, or None if not set."""
    cfg = get_config()
    return cfg.get("model") or None


def set_model(model: str) -> None:
    """Persist *model* as the default model in config.json."""
    cfg = get_config()
    cfg["model"] = model
    save_config(cfg)


def clear_model() -> None:
    """Remove the model override from config.json (resets to Claude Code default)."""
    cfg = get_config()
    cfg["model"] = None
    save_config(cfg)


def get_custom_models() -> list[dict]:
    """Return the list of user-added custom models from config.json."""
    cfg = get_config()
    custom = cfg.get("custom_models", [])
    return custom if isinstance(custom, list) else []


def add_custom_model(model_id: str, name: str = "", provider: str = "") -> None:
    """Add a custom model entry to config.json.

    If a model with the same ID already exists it is replaced.
    """
    cfg = get_config()
    custom: list[dict] = cfg.get("custom_models", [])
    if not isinstance(custom, list):
        custom = []
    # Remove existing entry with same id
    custom = [m for m in custom if m.get("id") != model_id]
    entry: dict = {"id": model_id}
    if name:
        entry["name"] = name
    if provider:
        entry["provider"] = provider
    custom.append(entry)
    cfg["custom_models"] = custom
    save_config(cfg)


def remove_custom_model(model_id: str) -> bool:
    """Remove a custom model by ID.  Returns True if it was found and removed."""
    cfg = get_config()
    custom: list[dict] = cfg.get("custom_models", [])
    if not isinstance(custom, list):
        return False
    new_custom = [m for m in custom if m.get("id") != model_id]
    if len(new_custom) == len(custom):
        return False
    cfg["custom_models"] = new_custom
    save_config(cfg)
    return True


# ---------------------------------------------------------------------------
# Multi-vault configuration helpers
# ---------------------------------------------------------------------------

_DEFAULT_VAULT: dict = {
    "url": "https://github.com/thebotclub/tribunal-vault",
    "priority": 0,
    "name": "community",
}


def get_vaults_config() -> list[dict]:
    """Return the ordered list of vault sources from config.json.

    Supports the new ``vaults`` array schema:
        { "vaults": [{ "url": "...", "priority": 1, "name": "..." }] }

    Falls back to the legacy single-vault ``repo_url``/``repo_branch`` keys
    for backward compatibility.

    Vaults are returned sorted by priority descending (higher = applied last,
    i.e. overrides lower-priority vaults).
    """
    cfg = get_config()
    vaults = cfg.get("vaults")
    if isinstance(vaults, list) and vaults:
        valid = [v for v in vaults if isinstance(v, dict) and v.get("url")]
        if valid:
            return sorted(valid, key=lambda v: int(v.get("priority", 0)))

    # Legacy fallback
    legacy: dict = dict(_DEFAULT_VAULT)
    legacy_url = cfg.get("repo_url")
    if legacy_url:
        legacy["url"] = str(legacy_url)
    return [legacy]
