from __future__ import annotations

import json
import tempfile
from pathlib import Path

import pytest

from mrp.contacts import Contact, ContactStore

GOLDEN_FILE = str(Path(__file__).parent / "../../testdata/contacts_golden.json")


class TestGoldenFile:
    """Cross-SDK interoperability: verify we can load the shared golden file."""

    def test_loads_golden_file(self):
        store = ContactStore.load(GOLDEN_FILE)
        assert len(store.list()) == 2

        alice = store.get("Alice")
        assert alice is not None
        assert alice.public_key == "ed25519:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik"
        assert alice.note == "translation agent"

        bob = store.get("Bob")
        assert bob is not None
        assert bob.public_key == "ed25519:Xk3m9nR4pQ7sT2wV5yA8bC1dE6fG0hI3jK4lM7nO9pQ"
        assert bob.note is None

        # Verify resolve works
        assert store.resolve("Alice") == alice.public_key
        assert store.resolve("bob") == bob.public_key


class TestDefaultPath:
    def test_returns_absolute_path(self):
        p = ContactStore.default_path()
        assert Path(p).is_absolute()
        assert p.endswith("contacts.json")


class TestLoad:
    def test_no_file(self, tmp_path):
        store = ContactStore.load(str(tmp_path / "missing.json"))
        assert store.list() == []

    def test_invalid_json(self, tmp_path):
        p = tmp_path / "bad.json"
        p.write_text("{bad}")
        with pytest.raises(ValueError, match="parsing contacts"):
            ContactStore.load(str(p))

    def test_loads_existing(self, tmp_path):
        p = tmp_path / "contacts.json"
        p.write_text(
            json.dumps(
                {
                    "contacts": [
                        {"name": "Alice", "public_key": "ed25519:alice", "note": "friend"},
                    ]
                }
            )
        )
        store = ContactStore.load(str(p))
        assert len(store.list()) == 1
        assert store.list()[0].name == "Alice"
        assert store.list()[0].public_key == "ed25519:alice"
        assert store.list()[0].note == "friend"


class TestSaveAndLoad:
    def test_round_trip(self, tmp_path):
        p = str(tmp_path / "contacts.json")
        store = ContactStore.load(p)
        store.add("Alice", "ed25519:alice-key", "test agent")
        store.add("Bob", "ed25519:bob-key")
        store.save()

        loaded = ContactStore.load(p)
        assert len(loaded.list()) == 2
        assert loaded.list()[0].name == "Alice"
        assert loaded.list()[0].note == "test agent"
        assert loaded.list()[1].name == "Bob"
        assert loaded.list()[1].note is None


class TestAdd:
    def test_adds_contact(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:key1")
        assert len(store.list()) == 1

    def test_duplicate_name(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:key1")
        with pytest.raises(ValueError, match="already exists"):
            store.add("alice", "ed25519:key2")

    def test_duplicate_key(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:key1")
        with pytest.raises(ValueError, match="already saved"):
            store.add("Bob", "ed25519:key1")

    def test_empty_name(self):
        store = ContactStore("/tmp/unused.json")
        with pytest.raises(ValueError, match="must not be empty"):
            store.add("", "ed25519:key1")

    def test_name_with_colon(self):
        store = ContactStore("/tmp/unused.json")
        with pytest.raises(ValueError, match="must not contain"):
            store.add("bad:name", "ed25519:key1")

    def test_name_too_long(self):
        store = ContactStore("/tmp/unused.json")
        with pytest.raises(ValueError, match="at most 64"):
            store.add("a" * 65, "ed25519:key1")


class TestRemove:
    def test_removes_case_insensitive(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:key1")
        store.add("Bob", "ed25519:key2")
        store.remove("alice")
        assert len(store.list()) == 1
        assert store.list()[0].name == "Bob"

    def test_not_found(self):
        store = ContactStore("/tmp/unused.json")
        with pytest.raises(ValueError, match="not found"):
            store.remove("nobody")


class TestResolve:
    def test_found_by_name(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:alice-key")
        assert store.resolve("Alice") == "ed25519:alice-key"

    def test_case_insensitive(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:alice-key")
        assert store.resolve("alice") == "ed25519:alice-key"

    def test_not_found_returns_input(self):
        store = ContactStore("/tmp/unused.json")
        assert store.resolve("nobody") == "nobody"

    def test_raw_key_passthrough(self):
        store = ContactStore("/tmp/unused.json")
        assert store.resolve("ed25519:raw-key") == "ed25519:raw-key"


class TestGet:
    def test_finds_by_name(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:key1", "note")
        c = store.get("alice")
        assert c is not None
        assert c.public_key == "ed25519:key1"

    def test_not_found(self):
        store = ContactStore("/tmp/unused.json")
        assert store.get("nobody") is None


class TestList:
    def test_returns_all(self):
        store = ContactStore("/tmp/unused.json")
        store.add("Alice", "ed25519:key1")
        store.add("Bob", "ed25519:key2")
        assert len(store.list()) == 2
