"""Local contact store for mapping friendly names to public keys.

Contacts are stored as JSON at ~/.mrp/contacts.json and shared across
the CLI, MCP server, and OpenClaw plugin.
"""

from __future__ import annotations

import json
import os
from dataclasses import dataclass
from pathlib import Path


@dataclass
class Contact:
    """A saved contact mapping a friendly name to a public key."""

    name: str
    public_key: str
    note: str | None = None


class ContactStore:
    """Manages a local contact list persisted as JSON.

    The contact file is shared with the MRP CLI at ``~/.mrp/contacts.json``.
    """

    def __init__(self, path: str, contacts: list[Contact] | None = None) -> None:
        self._path = path
        self._contacts: list[Contact] = contacts or []

    @staticmethod
    def default_path() -> str:
        """Return the default contacts file path (~/.mrp/contacts.json)."""
        return os.path.join(Path.home(), ".mrp", "contacts.json")

    @classmethod
    def load(cls, path: str | None = None) -> ContactStore:
        """Load contacts from *path* (or the default).

        Returns an empty store if the file does not exist.
        """
        p = path or cls.default_path()
        try:
            data = json.loads(Path(p).read_text(encoding="utf-8"))
        except FileNotFoundError:
            return cls(p)
        except (json.JSONDecodeError, OSError) as exc:
            raise ValueError(f"parsing contacts: {exc}") from exc

        contacts: list[Contact] = []
        for entry in data.get("contacts", []):
            contacts.append(
                Contact(
                    name=entry["name"],
                    public_key=entry["public_key"],
                    note=entry.get("note"),
                )
            )
        return cls(p, contacts)

    def save(self) -> None:
        """Write the contact store to disk."""
        path = Path(self._path)
        path.parent.mkdir(parents=True, mode=0o700, exist_ok=True)
        entries = []
        for c in self._contacts:
            entry: dict[str, str] = {"name": c.name, "public_key": c.public_key}
            if c.note:
                entry["note"] = c.note
            entries.append(entry)
        data = json.dumps({"contacts": entries}, indent=2) + "\n"
        path.write_text(data, encoding="utf-8")

    def add(self, name: str, public_key: str, note: str | None = None) -> None:
        """Add a contact. Raises ``ValueError`` on duplicates or invalid names."""
        _validate_contact_name(name)
        lower = name.lower()
        for c in self._contacts:
            if c.name.lower() == lower:
                raise ValueError(f'contact "{c.name}" already exists')
            if c.public_key == public_key:
                raise ValueError(f'public key already saved as "{c.name}"')
        self._contacts.append(Contact(name=name, public_key=public_key, note=note))

    def remove(self, name: str) -> None:
        """Remove a contact by name (case-insensitive)."""
        lower = name.lower()
        for i, c in enumerate(self._contacts):
            if c.name.lower() == lower:
                del self._contacts[i]
                return
        raise ValueError(f'contact "{name}" not found')

    def resolve(self, name_or_key: str) -> str:
        """Map a name or key to a public key.

        If *name_or_key* contains ``:``, it is treated as a raw key and
        returned as-is.  Otherwise a case-insensitive name lookup is
        performed.  If no match is found the original input is returned
        unchanged.
        """
        if ":" in name_or_key:
            return name_or_key
        lower = name_or_key.lower()
        for c in self._contacts:
            if c.name.lower() == lower:
                return c.public_key
        return name_or_key

    def list(self) -> list[Contact]:
        """Return all contacts."""
        return list(self._contacts)

    def get(self, name: str) -> Contact | None:
        """Get a contact by name (case-insensitive), or ``None``."""
        lower = name.lower()
        for c in self._contacts:
            if c.name.lower() == lower:
                return c
        return None


def _validate_contact_name(name: str) -> None:
    if not name:
        raise ValueError("contact name must not be empty")
    if len(name) > 64:
        raise ValueError("contact name must be at most 64 characters")
    if ":" in name:
        raise ValueError("contact name must not contain ':'")
