"""Load environment and agent templates from the package and optionally from a local directory."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

# Name of the per-workspace directory that may override/extend built-in
# templates.  It lives inside the (agent-writable) workspace, so it is one of
# the trust-sensitive config inputs masked from the agent at runtime.
LOCAL_TEMPLATES_DIRNAME = "templates"

_BUILTIN_DIR = Path(__file__).resolve().parent / "data" / "templates"
_ENV_DIR = _BUILTIN_DIR / "environments"
_AGENT_DIR = _BUILTIN_DIR / "agents"


class TemplateNotFoundError(FileNotFoundError):
    pass


def _load(path: Path) -> dict[str, Any]:
    with open(path, encoding="utf-8") as fh:
        return json.load(fh)


def _local_dir(workspace: Path | None) -> Path | None:
    if workspace is not None:
        candidate = workspace / LOCAL_TEMPLATES_DIRNAME
        if candidate.is_dir():
            return candidate
    return None


def _search_local(local_dir: Path | None, name: str, template_type: str) -> Path | None:
    if local_dir is None:
        return None
    for json_file in local_dir.glob("*.json"):
        try:
            data = _load(json_file)
        except (json.JSONDecodeError, OSError):
            continue
        if data.get("template_type") == template_type and data.get("name") == name:
            return json_file
    return None


def _load_direct_file(name: str, workspace: Path | None, template_type: str) -> dict[str, Any]:
    """Load ``name`` as a local .json file instead of a built-in template."""
    candidates = [Path(name)]
    if workspace is not None and not Path(name).is_absolute():
        candidates.append(workspace / name)
    for candidate in candidates:
        if candidate.is_file():
            return _load(candidate)
    raise TemplateNotFoundError(f"{template_type.title()} template file '{name}' not found")


def load_environment(name: str, workspace: Path | None = None) -> dict[str, Any]:
    if name.endswith(".json"):
        return _load_direct_file(name, workspace, "environment")
    local_dir = _local_dir(workspace)
    local_path = _search_local(local_dir, name, "environment")
    if local_path:
        return _load(local_path)
    builtin = _ENV_DIR / f"{name}.json"
    if builtin.exists():
        return _load(builtin)
    raise TemplateNotFoundError(f"Environment template '{name}' not found")


def load_agent(name: str, workspace: Path | None = None) -> dict[str, Any]:
    if name.endswith(".json"):
        return _load_direct_file(name, workspace, "agent")
    local_dir = _local_dir(workspace)
    local_path = _search_local(local_dir, name, "agent")
    if local_path:
        return _load(local_path)
    builtin = _AGENT_DIR / f"{name}.json"
    if builtin.exists():
        return _load(builtin)
    raise TemplateNotFoundError(f"Agent template '{name}' not found")


def list_environments(workspace: Path | None = None) -> list[tuple[str, str]]:
    seen: dict[str, str] = {}
    local_dir = _local_dir(workspace)
    if local_dir:
        for p in local_dir.glob("*.json"):
            try:
                data = _load(p)
            except (json.JSONDecodeError, OSError):
                continue
            if data.get("template_type") == "environment":
                seen[data["name"]] = data.get("base-image", "?")
    for p in sorted(_ENV_DIR.glob("*.json")):
        data = _load(p)
        seen.setdefault(data["name"], data.get("base-image", "?"))
    return sorted(seen.items())


def list_agents(workspace: Path | None = None) -> list[str]:
    seen: set[str] = set()
    local_dir = _local_dir(workspace)
    if local_dir:
        for p in local_dir.glob("*.json"):
            try:
                data = _load(p)
            except (json.JSONDecodeError, OSError):
                continue
            if data.get("template_type") == "agent":
                seen.add(data["name"])
    for p in sorted(_AGENT_DIR.glob("*.json")):
        data = _load(p)
        seen.add(data["name"])
    return sorted(seen)
