"""Ed25519 key generation and request signing for Sentinel API authentication.

Provides:
- generate_keypair() — Create a new Ed25519 keypair for agent identity.
- sign_request()     — Sign API requests using the canonical message format
                       expected by AgentAuthenticationService.verifySignature():

    <timestamp>\\n<HTTP_METHOD>\\n<path_with_query>\\n<sha256(body)>

The path MUST include the query string if present (e.g. /api/v1/markets?status=ACTIVE).
This prevents parameter-tampering attacks where a valid signature is replayed
with modified query parameters.

The signature is returned as Base64 (matching BouncyCastle Ed25519
verification on the backend).
"""

import base64
import hashlib
from datetime import datetime, timezone

from nacl.signing import SigningKey


def generate_keypair() -> dict[str, str]:
    """Generate a new Ed25519 keypair for agent identity.

    Returns:
        Dict with:
        - public_key: 64-char hex string (32 bytes)
        - private_key: 128-char hex string (64 bytes — seed + public key)
        - created_at: ISO-8601 timestamp
    """
    signing_key = SigningKey.generate()
    # Full 64-byte private key: 32-byte seed + 32-byte public key
    private_key_hex = (signing_key.encode() + signing_key.verify_key.encode()).hex()
    public_key_hex = signing_key.verify_key.encode().hex()

    return {
        "public_key": public_key_hex,
        "private_key": private_key_hex,
        "created_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
    }


def sign_request(
    method: str,
    path: str,
    body: str | None,
    private_key_hex: str,
) -> dict[str, str]:
    """Sign a Sentinel API request with Ed25519.

    Args:
        method: HTTP method (GET, POST, etc.)
        path: Full request path including query string (e.g. /api/v1/trade
              or /api/v1/markets?status=ACTIVE)
        body: Request body string, or None for bodyless requests
        private_key_hex: 128-char hex private key (64-byte Ed25519 key).
                         PyNaCl uses the first 32 bytes (seed).

    Returns:
        Dict with 'timestamp' (ISO-8601) and 'signature' (Base64-encoded).
    """
    timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

    body_bytes = (body or "").encode("utf-8")
    body_hash = hashlib.sha256(body_bytes).hexdigest()

    # Canonical message: timestamp\nMETHOD\n/path?query\nsha256(body)
    message = f"{timestamp}\n{method}\n{path}\n{body_hash}"
    message_bytes = message.encode("utf-8")

    # PyNaCl expects the 32-byte seed (first half of the 64-byte private key)
    seed = bytes.fromhex(private_key_hex[:64])
    signing_key = SigningKey(seed)
    signed = signing_key.sign(message_bytes)

    return {
        "timestamp": timestamp,
        "signature": base64.b64encode(signed.signature).decode("ascii"),
    }
