"""Tests for PeerID <-> did:key and did:web conversions."""

import pytest

from agentanycast.did import (
    did_key_to_peer_id,
    did_web_to_url,
    peer_id_to_did_key,
    url_to_did_web,
)

# A well-known Ed25519 PeerID generated by libp2p (identity multihash format).
# This was produced by the Go daemon and verified against the DID spec.


def _make_peer_id(pubkey_bytes: bytes) -> str:
    """Build a libp2p PeerID from raw 32-byte Ed25519 public key."""
    import base58

    # Protobuf: field 1 (KeyType=Ed25519=1) = 0x08 0x01, field 2 (Data) = 0x12 <len> <data>
    proto = bytes([0x08, 0x01, 0x12, len(pubkey_bytes)]) + pubkey_bytes
    # Identity multihash: 0x00 <length> <data>
    mh = bytes([0x00, len(proto)]) + proto
    return base58.b58encode(mh).decode("ascii")


def _make_did_key(pubkey_bytes: bytes) -> str:
    """Build a did:key from raw 32-byte Ed25519 public key."""
    import base58

    mc = bytes([0xED, 0x01]) + pubkey_bytes
    return "did:key:z" + base58.b58encode(mc).decode("ascii")


class TestPeerIDToDIDKey:
    def test_valid_ed25519(self):
        pubkey = bytes(range(32))
        peer_id = _make_peer_id(pubkey)
        did = peer_id_to_did_key(peer_id)
        assert did.startswith("did:key:z")

    def test_prefix_format(self):
        pubkey = bytes(range(32))
        peer_id = _make_peer_id(pubkey)
        did = peer_id_to_did_key(peer_id)
        # Multibase base58btc prefix 'z' after did:key:
        assert did[8] == "z"

    def test_invalid_peer_id_raises(self):
        with pytest.raises(ValueError):
            peer_id_to_did_key("not-a-valid-peer-id!!!")


class TestDIDKeyToPeerID:
    def test_valid_did_key(self):
        pubkey = bytes(range(32))
        did = _make_did_key(pubkey)
        peer_id = did_key_to_peer_id(did)
        assert len(peer_id) > 0

    def test_invalid_prefix_raises(self):
        with pytest.raises(ValueError, match="invalid did:key format"):
            did_key_to_peer_id("not-a-did")

    def test_too_short_raises(self):
        import base58

        short = base58.b58encode(bytes([0xED, 0x01, 0x00])).decode("ascii")
        with pytest.raises(ValueError, match="too short"):
            did_key_to_peer_id(f"did:key:z{short}")

    def test_wrong_multicodec_raises(self):
        import base58

        wrong = bytes([0x00, 0x01]) + bytes(32)
        encoded = base58.b58encode(wrong).decode("ascii")
        with pytest.raises(ValueError, match="unsupported multicodec"):
            did_key_to_peer_id(f"did:key:z{encoded}")


class TestRoundTrip:
    def test_peer_id_to_did_key_and_back(self):
        pubkey = bytes(range(32))
        peer_id = _make_peer_id(pubkey)
        did = peer_id_to_did_key(peer_id)
        recovered = did_key_to_peer_id(did)
        assert recovered == peer_id

    def test_did_key_to_peer_id_and_back(self):
        pubkey = bytes(range(32))
        did = _make_did_key(pubkey)
        peer_id = did_key_to_peer_id(did)
        recovered = peer_id_to_did_key(peer_id)
        assert recovered == did

    def test_multiple_keys_round_trip(self):
        """Verify round-trip works for multiple different keys."""
        for i in range(10):
            pubkey = bytes([(i * 7 + j) % 256 for j in range(32)])
            peer_id = _make_peer_id(pubkey)
            did = peer_id_to_did_key(peer_id)
            recovered = did_key_to_peer_id(did)
            assert recovered == peer_id, f"round-trip failed for key {i}"

    def test_consistency_with_known_key(self):
        """Both directions produce consistent results for a zero key."""
        pubkey = bytes(32)  # all zeros
        peer_id = _make_peer_id(pubkey)
        did = _make_did_key(pubkey)

        assert peer_id_to_did_key(peer_id) == did
        assert did_key_to_peer_id(did) == peer_id


class TestDIDWebToUrl:
    def test_domain_only(self):
        assert did_web_to_url("did:web:example.com") == ("https://example.com/.well-known/did.json")

    def test_domain_with_path(self):
        assert did_web_to_url("did:web:example.com:agents:myagent") == (
            "https://example.com/agents/myagent/did.json"
        )

    def test_domain_with_port(self):
        # Ports are percent-encoded in did:web — e.g. localhost%3A8080
        assert did_web_to_url("did:web:localhost%3A8080") == (
            "https://localhost:8080/.well-known/did.json"
        )

    def test_domain_with_port_and_path(self):
        assert did_web_to_url("did:web:localhost%3A8080:api:v1") == (
            "https://localhost:8080/api/v1/did.json"
        )

    def test_invalid_prefix_raises(self):
        with pytest.raises(ValueError, match="invalid did:web format"):
            did_web_to_url("did:key:zSomething")


class TestUrlToDidWeb:
    def test_domain_only(self):
        assert url_to_did_web("https://example.com/.well-known/did.json") == ("did:web:example.com")

    def test_domain_with_path(self):
        assert url_to_did_web("https://example.com/agents/myagent/did.json") == (
            "did:web:example.com:agents:myagent"
        )

    def test_domain_with_port(self):
        url = "https://localhost:8080/.well-known/did.json"
        assert url_to_did_web(url) == "did:web:localhost%3A8080"

    def test_non_https_raises(self):
        with pytest.raises(ValueError, match="HTTPS"):
            url_to_did_web("http://example.com/.well-known/did.json")

    def test_bad_path_raises(self):
        with pytest.raises(ValueError, match="did.json"):
            url_to_did_web("https://example.com/some/random/path")


class TestDIDWebRoundTrip:
    def test_domain_only_round_trip(self):
        did = "did:web:example.com"
        assert url_to_did_web(did_web_to_url(did)) == did

    def test_path_based_round_trip(self):
        did = "did:web:example.com:agents:myagent"
        assert url_to_did_web(did_web_to_url(did)) == did

    def test_port_round_trip(self):
        did = "did:web:localhost%3A8080"
        assert url_to_did_web(did_web_to_url(did)) == did
