"""Golden vector tests: Python SDK must reproduce the server's EIP-712 hashes exactly.

These tests load test-vectors/eip712.json (generated by `cargo run --bin gen_vectors`
in remit-server) and verify that:
  1. The Python SDK computes the same EIP-712 hash for each vector's inputs.
  2. The Python SDK produces the same ECDSA signature when signing with the same key.

If any assertion fails, the Python SDK's EIP-712 implementation diverges from the
server and authentication WILL fail in production.
"""

from __future__ import annotations

import json
from pathlib import Path

import pytest
from eth_account import Account
from eth_account.messages import encode_typed_data

# ─── Vector file ──────────────────────────────────────────────────────────────

# Vectors live in test-vectors/ at the sdk repo root.
_VECTORS_PATH = Path(__file__).parent.parent.parent / "test-vectors" / "eip712.json"

# Anvil test wallet #0 — same key used by gen_vectors in remit-server.
_TEST_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# EIP-712 typed struct — must match server's auth.rs exactly.
_AUTH_TYPES = {
    "APIRequest": [
        {"name": "method", "type": "string"},
        {"name": "path", "type": "string"},
        {"name": "timestamp", "type": "uint256"},
        {"name": "nonce", "type": "bytes32"},
    ]
}


def _load_vectors() -> list[dict]:  # type: ignore[type-arg]
    assert _VECTORS_PATH.exists(), (
        f"Golden vector file not found at {_VECTORS_PATH}. "
        "Run `cargo run --bin gen_vectors > test-vectors/eip712.json` in remit-server."
    )
    with _VECTORS_PATH.open() as f:
        data = json.load(f)
    vectors = data["vectors"]
    assert len(vectors) > 0, "vectors array must not be empty"
    return vectors  # type: ignore[return-value]


# ─── Tests ────────────────────────────────────────────────────────────────────


@pytest.mark.parametrize("vector", _load_vectors(), ids=lambda v: v["description"])
def test_eip712_hash_matches_server(vector: dict) -> None:  # type: ignore[type-arg]
    """Python SDK must produce the same EIP-712 hash as the server for each vector."""
    domain = {
        "name": vector["domain"]["name"],
        "version": vector["domain"]["version"],
        "chainId": vector["domain"]["chain_id"],
        "verifyingContract": vector["domain"]["verifying_contract"],
    }
    value = {
        "method": vector["message"]["method"],
        "path": vector["message"]["path"],
        "timestamp": vector["message"]["timestamp"],
        "nonce": vector["message"]["nonce"],
    }

    structured = encode_typed_data(
        domain_data=domain,
        message_types=_AUTH_TYPES,
        message_data=value,
    )
    signed = Account.sign_message(structured, private_key=_TEST_PRIVATE_KEY)

    got_hash = "0x" + signed.message_hash.hex()
    assert got_hash == vector["expected_hash"], (
        f"EIP-712 hash mismatch for vector '{vector['description']}':\n"
        f"  got:      {got_hash}\n"
        f"  expected: {vector['expected_hash']}"
    )


@pytest.mark.parametrize("vector", _load_vectors(), ids=lambda v: v["description"])
def test_signature_matches_server(vector: dict) -> None:  # type: ignore[type-arg]
    """Python SDK must produce the same ECDSA signature as the server for each vector."""
    domain = {
        "name": vector["domain"]["name"],
        "version": vector["domain"]["version"],
        "chainId": vector["domain"]["chain_id"],
        "verifyingContract": vector["domain"]["verifying_contract"],
    }
    value = {
        "method": vector["message"]["method"],
        "path": vector["message"]["path"],
        "timestamp": vector["message"]["timestamp"],
        "nonce": vector["message"]["nonce"],
    }

    structured = encode_typed_data(
        domain_data=domain,
        message_types=_AUTH_TYPES,
        message_data=value,
    )
    signed = Account.sign_message(structured, private_key=_TEST_PRIVATE_KEY)

    got_sig = "0x" + signed.signature.hex()
    assert got_sig == vector["expected_signature"], (
        f"Signature mismatch for vector '{vector['description']}':\n"
        f"  got:      {got_sig}\n"
        f"  expected: {vector['expected_signature']}"
    )
