"""Tests for RSA key generation and HTTP Signature signing/verification."""
from __future__ import annotations

import json

from shared.utils.http_signatures import (
    generate_rsa_keypair,
    sign_request,
    verify_request_signature,
    create_ld_signature,
)


# ---------------------------------------------------------------------------
# Key generation
# ---------------------------------------------------------------------------

class TestKeyGeneration:
    def test_generates_pem_strings(self):
        private_pem, public_pem = generate_rsa_keypair()
        assert isinstance(private_pem, str)
        assert isinstance(public_pem, str)

    def test_private_key_format(self):
        private_pem, _ = generate_rsa_keypair()
        assert "BEGIN PRIVATE KEY" in private_pem
        assert "END PRIVATE KEY" in private_pem

    def test_public_key_format(self):
        _, public_pem = generate_rsa_keypair()
        assert "BEGIN PUBLIC KEY" in public_pem
        assert "END PUBLIC KEY" in public_pem

    def test_keys_are_unique(self):
        priv1, pub1 = generate_rsa_keypair()
        priv2, pub2 = generate_rsa_keypair()
        assert priv1 != priv2
        assert pub1 != pub2


# ---------------------------------------------------------------------------
# Sign + verify round-trip
# ---------------------------------------------------------------------------

class TestSignVerify:
    def test_round_trip_no_body(self):
        private_pem, public_pem = generate_rsa_keypair()
        headers = sign_request(
            private_pem, key_id="https://example.com/users/alice#main-key",
            method="GET", path="/users/bob/inbox", host="example.com",
            date="Sat, 15 Jun 2025 12:00:00 GMT",
        )
        assert "Signature" in headers
        assert "Date" in headers
        assert "Host" in headers
        assert "Digest" not in headers

        ok = verify_request_signature(
            public_pem, headers["Signature"], method="GET",
            path="/users/bob/inbox", headers=headers,
        )
        assert ok is True

    def test_round_trip_with_body(self):
        private_pem, public_pem = generate_rsa_keypair()
        body = b'{"type": "Follow"}'
        headers = sign_request(
            private_pem, key_id="https://example.com/users/alice#main-key",
            method="POST", path="/users/bob/inbox", host="example.com",
            body=body, date="Sat, 15 Jun 2025 12:00:00 GMT",
        )
        assert "Digest" in headers
        assert headers["Digest"].startswith("SHA-256=")

        ok = verify_request_signature(
            public_pem, headers["Signature"], method="POST",
            path="/users/bob/inbox", headers=headers,
        )
        assert ok is True

    def test_wrong_key_fails(self):
        priv1, _ = generate_rsa_keypair()
        _, pub2 = generate_rsa_keypair()
        headers = sign_request(
            priv1, key_id="key1", method="GET", path="/inbox", host="a.com",
            date="Sat, 15 Jun 2025 12:00:00 GMT",
        )
        ok = verify_request_signature(pub2, headers["Signature"], "GET", "/inbox", headers)
        assert ok is False

    def test_tampered_path_fails(self):
        private_pem, public_pem = generate_rsa_keypair()
        headers = sign_request(
            private_pem, key_id="key1", method="GET", path="/inbox", host="a.com",
            date="Sat, 15 Jun 2025 12:00:00 GMT",
        )
        ok = verify_request_signature(public_pem, headers["Signature"], "GET", "/tampered", headers)
        assert ok is False

    def test_tampered_method_fails(self):
        private_pem, public_pem = generate_rsa_keypair()
        headers = sign_request(
            private_pem, key_id="key1", method="GET", path="/inbox", host="a.com",
            date="Sat, 15 Jun 2025 12:00:00 GMT",
        )
        ok = verify_request_signature(public_pem, headers["Signature"], "POST", "/inbox", headers)
        assert ok is False

    def test_signature_header_contains_key_id(self):
        private_pem, _ = generate_rsa_keypair()
        headers = sign_request(
            private_pem, key_id="https://my.server/actor#main-key",
            method="POST", path="/inbox", host="remote.server",
            date="Sat, 15 Jun 2025 12:00:00 GMT",
        )
        assert 'keyId="https://my.server/actor#main-key"' in headers["Signature"]
        assert 'algorithm="rsa-sha256"' in headers["Signature"]


# ---------------------------------------------------------------------------
# Linked Data signature
# ---------------------------------------------------------------------------

class TestLDSignature:
    def test_creates_ld_signature(self):
        private_pem, _ = generate_rsa_keypair()
        activity = {"type": "Create", "actor": "https://example.com/users/alice"}
        sig = create_ld_signature(private_pem, "https://example.com/users/alice#main-key", activity)
        assert sig["type"] == "RsaSignature2017"
        assert sig["creator"] == "https://example.com/users/alice#main-key"
        assert "signatureValue" in sig
        assert "created" in sig

    def test_deterministic_canonical(self):
        """Same activity always produces same canonical form (signature differs due to timestamp)."""
        private_pem, _ = generate_rsa_keypair()
        activity = {"b": 2, "a": 1}
        # The canonical form should sort keys
        canonical = json.dumps(activity, sort_keys=True, separators=(",", ":"))
        assert canonical == '{"a":1,"b":2}'
