"""User-level registry for named external projects."""

from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Dict, List, Optional

from platformdirs import user_data_dir


APP_NAME = "externai"
APP_AUTHOR = "ki2"
REGISTRY_FILENAME = "registry.json"


def _registry_dir() -> Path:
    """
    Resolve and create the directory used to store the registry file.

    Returns:
        Writable directory path under the user data location.
    """
    base = Path(user_data_dir(APP_NAME, APP_AUTHOR))
    base.mkdir(parents=True, exist_ok=True)
    return base


def _registry_file() -> Path:
    """
    Build the full path to the registry JSON file.

    Returns:
        File path pointing to `registry.json` under the registry directory.
    """
    return _registry_dir() / REGISTRY_FILENAME


def load_registry() -> Dict[str, str]:
    """
    Load registry entries from disk.

    Returns:
        Mapping of project names to absolute source paths.

    Raises:
        SystemExit: Raised when the JSON structure is invalid.
    """
    path = _registry_file()
    if not path.exists():
        return {}
    try:
        raw = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        sys.exit(f"[registry] Invalid JSON in {path}: {exc}")
    if raw is None:
        return {}
    if not isinstance(raw, dict):
        sys.exit("[registry] Registry file must contain a JSON object {name: path}.")
    data: Dict[str, str] = {}
    for name, value in raw.items():
        if not isinstance(name, str) or not isinstance(value, str):
            sys.exit("[registry] Registry entries must be string name/path pairs.")
        data[name] = value
    return data


def save_registry(data: Dict[str, str]) -> None:
    """
    Save registry entries to disk with deterministic ordering.

    Args:
        data: Mapping of project names to source paths.
    """
    path = _registry_file()
    path.write_text(json.dumps(data, indent=2, sort_keys=True), encoding="utf-8")


def resolve_registered_path(name: str, data: Optional[Dict[str, str]] = None) -> Optional[str]:
    """
    Resolve a registered path by name.

    Args:
        name: Registry key to resolve.
        data: Optional in-memory mapping to use instead of reading from disk.

    Returns:
        Registered path when found, otherwise None.
    """
    source = data if data is not None else load_registry()
    return source.get(name)


def normalize_path(path: Path) -> str:
    """
    Convert a path to a normalized absolute string representation.

    Args:
        path: Path to normalize.

    Returns:
        Resolved absolute path string.
    """
    return str(path.resolve())


def find_names_for_path(path: Path, data: Optional[Dict[str, str]] = None) -> List[str]:
    """
    Find all registry names that point to a given filesystem path.

    Args:
        path: Path to match against registry entries.
        data: Optional in-memory mapping to use instead of reading from disk.

    Returns:
        List of matching registry names.
    """
    source = data if data is not None else load_registry()
    target = Path(path).resolve()
    names: List[str] = []
    for name, stored in source.items():
        try:
            stored_path = Path(stored).resolve()
        except OSError:
            stored_path = Path(stored)
        if stored_path == target:
            names.append(name)
    return names
