"""Storage for linked repositories used as multi-repo context in test generation."""

import json
import os
from datetime import datetime, timezone
from pathlib import Path


class LinkedRepoStore:
    """Manage a list of repos linked to the current repo for test-generation context.

    Links are stored per-repo at ``$MAERIS_CONFIG_DIR/links.json``, which mirrors
    the same isolation model as credentials (keyed by repo path hash via
    MAERIS_CONFIG_DIR in .mcp.json).
    """

    def __init__(self, config_dir: Path | None = None) -> None:
        if config_dir:
            self.config_dir = config_dir
        elif env_dir := os.getenv("MAERIS_CONFIG_DIR"):
            self.config_dir = Path(env_dir)
        else:
            self.config_dir = Path.home() / ".maeris"
        self.links_file = self.config_dir / "links.json"

    # ------------------------------------------------------------------
    # Internal helpers
    # ------------------------------------------------------------------

    def _load(self) -> list[dict]:
        if not self.links_file.exists():
            return []
        try:
            data = json.loads(self.links_file.read_text(encoding="utf-8"))
            return data if isinstance(data, list) else []
        except Exception:
            return []

    def _save(self, links: list[dict]) -> None:
        self.config_dir.mkdir(parents=True, exist_ok=True)
        self.links_file.write_text(json.dumps(links, indent=2), encoding="utf-8")

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def add(self, path: str, label: str | None = None) -> dict:
        """Register a repo as a context source.

        Args:
            path: Absolute or relative path to the linked repo root.
            label: Human-readable name. Defaults to the directory name.

        Returns:
            The stored entry dict.
        """
        abs_path = str(Path(path).expanduser().resolve())
        label = label or Path(abs_path).name
        links = self._load()
        # Overwrite any existing entry for the same path
        links = [lk for lk in links if lk["path"] != abs_path]
        entry: dict = {
            "path": abs_path,
            "label": label,
            "added": datetime.now(timezone.utc).isoformat(),
        }
        links.append(entry)
        self._save(links)
        return entry

    def remove(self, path_or_label: str) -> bool:
        """Remove a linked repo by path or label.

        Returns:
            True if an entry was removed, False if nothing matched.
        """
        candidate = str(Path(path_or_label).expanduser().resolve())
        links = self._load()
        before = len(links)
        # Match by resolved path first, then by label
        filtered = [lk for lk in links if lk["path"] != candidate and lk["label"] != path_or_label]
        if len(filtered) == before:
            return False
        self._save(filtered)
        return True

    def list_all(self) -> list[dict]:
        """Return all linked repos, annotated with an ``exists`` boolean."""
        links = self._load()
        for lk in links:
            lk["exists"] = Path(lk["path"]).is_dir()
        return links

    def list_valid(self) -> list[dict]:
        """Return only linked repos whose path still exists on disk."""
        return [lk for lk in self.list_all() if lk.get("exists")]
