"""
Coverage tests for keys.py - untested branches.
"""

import pytest

from bsv.keys import PrivateKey, PublicKey

# ========================================================================
# PrivateKey initialization branches
# ========================================================================

# Constants for skip messages
TEST_MESSAGE_BYTES = b"test message"
TEST_MESSAGE_BYTES2 = b"test message"
SKIP_SIGNATURE_OPS = "signature operations not available"
SKIP_KEY_SHARING = "key sharing operations not available"


def test_private_key_init_none():
    """Test PrivateKey with None (generates random)."""
    key = PrivateKey()
    assert key  # Verify object creation succeeds
    assert key.serialize()  # Verify serialization produces output


def test_private_key_init_with_bytes():
    """Test PrivateKey with specific bytes."""
    key_bytes = b"\x01" * 32
    key = PrivateKey(key_bytes)
    assert key.serialize() == key_bytes


def test_private_key_init_with_int():
    """Test PrivateKey with integer."""
    key = PrivateKey(1)
    assert hasattr(key, "wif")


def test_private_key_init_with_large_int():
    """Test PrivateKey with large integer within curve order."""
    # Use a value within the secp256k1 curve order
    curve_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
    key = PrivateKey(curve_order - 1)  # Valid value just below curve order
    assert hasattr(key, "wif")


# ========================================================================
# PrivateKey methods
# ========================================================================


def test_private_key_to_public_key():
    """Test converting private key to public key."""
    priv = PrivateKey()
    pub = priv.public_key()
    assert isinstance(pub, PublicKey)


def test_private_key_to_wif():
    """Test private key to WIF."""
    priv = PrivateKey(b"\x01" * 32)
    wif = priv.wif()
    assert isinstance(wif, str)
    assert len(wif) > 0


def test_private_key_from_wif():
    """Test creating private key from WIF."""
    priv1 = PrivateKey(b"\x01" * 32)
    wif = priv1.wif()
    priv2 = PrivateKey(wif)  # Constructor accepts WIF string
    assert priv1.serialize() == priv2.serialize()


def test_private_key_sign():
    """Test private key signing."""
    priv = PrivateKey()
    message = TEST_MESSAGE_BYTES
    signature = priv.sign(message)
    assert isinstance(signature, bytes)
    assert len(signature) > 0


# ========================================================================
# Comprehensive error condition testing and branch coverage
# ========================================================================


def test_private_key_sign_with_empty_message():
    """Test signing with empty message."""
    try:
        priv = PrivateKey()
        message = b""

        signature = priv.sign(message)
        assert isinstance(signature, bytes)
        assert len(signature) > 0
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_private_key_sign_with_large_message():
    """Test signing with large message."""
    try:
        priv = PrivateKey()
        message = b"\x01" * 10000  # Large message

        signature = priv.sign(message)
        assert isinstance(signature, bytes)
        assert len(signature) > 0
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_private_key_sign_canonical_low_s():
    """Test signing produces canonical low-S signatures."""
    try:
        priv = PrivateKey()
        message = TEST_MESSAGE_BYTES2
        signature = priv.sign(message)

        # Parse DER signature to check S value
        if len(signature) > 8:  # Valid DER signature
            # Simple check - if we can parse it, it's likely canonical
            assert isinstance(signature, bytes)
            assert len(signature) > 0
    except ImportError:
        pytest.skip("signature parsing not available")


def test_private_key_sign_msb_prefix_r():
    """Test signing with MSB prefix for r value."""
    try:
        priv = PrivateKey()
        message = TEST_MESSAGE_BYTES2
        signature = priv.sign(message)

        # Check if signature is properly formatted
        assert isinstance(signature, bytes)
        assert len(signature) > 0

        # DER format should start with 0x30
        if len(signature) > 0:
            assert signature[0] == 0x30  # DER sequence
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_private_key_sign_msb_prefix_s():
    """Test signing with MSB prefix for s value."""
    try:
        priv = PrivateKey()
        message = TEST_MESSAGE_BYTES2
        signature = priv.sign(message)

        # Check if signature is properly formatted
        assert isinstance(signature, bytes)
        assert len(signature) > 0
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_key_shares_generation_failure():
    """Test key shares generation failure after max attempts."""
    try:
        from unittest.mock import patch

        from bsv.keys import curve

        priv = PrivateKey()

        # Mock to always return the same x coordinate, causing collision
        with patch("os.urandom", return_value=b"\x01" * 32):  # Same seed each time
            with patch("bsv.keys.hmac_sha512", return_value=b"\x01" * 64):  # Same HMAC each time
                with pytest.raises(ValueError, match="Failed to generate unique x coordinate"):
                    priv.to_key_shares(2, 3)  # 2-of-3 shares
    except ImportError:
        pytest.skip(SKIP_KEY_SHARING)


def test_key_shares_invalid_threshold():
    """Test key shares with invalid threshold (< 2)."""
    try:
        from bsv.keys import KeyShares, PrivateKey

        # Create a valid KeyShares object first
        priv = PrivateKey()
        key_shares = priv.to_key_shares(2, 3)

        # Now modify it to have invalid threshold and try to reconstruct
        key_shares.threshold = 1  # Invalid threshold

        with pytest.raises(ValueError, match="threshold must be at least 2"):
            PrivateKey.from_key_shares(key_shares)
    except ImportError:
        pytest.skip("KeyShares not available")


def test_key_shares_insufficient_points():
    """Test key shares reconstruction with insufficient points."""
    try:
        from bsv.keys import KeyShares, PointInFiniteField

        # Create key shares with threshold 3 but only 2 points
        points = [PointInFiniteField(1, 2), PointInFiniteField(3, 4)]
        key_shares = KeyShares(points, 3, "integrity")

        with pytest.raises(ValueError, match="At least 3 shares are required"):
            PrivateKey.from_key_shares(key_shares)
    except ImportError:
        pytest.skip(SKIP_KEY_SHARING)


def test_key_shares_integrity_mismatch():
    """Test key shares with integrity hash mismatch."""
    try:
        from unittest.mock import patch

        from bsv.keys import KeyShares, PointInFiniteField

        points = [PointInFiniteField(1, 2), PointInFiniteField(3, 4), PointInFiniteField(5, 6)]
        key_shares = KeyShares(points, 2, "integrity")

        # Mock integrity check to fail
        with patch("bsv.keys.hash160") as mock_hash:
            mock_hash.return_value = b"different_hash"
            with pytest.raises(ValueError, match="Integrity hash mismatch"):
                PrivateKey.from_key_shares(key_shares)
    except ImportError:
        pytest.skip(SKIP_KEY_SHARING)


def test_private_key_invalid_initialization():
    """Test PrivateKey with invalid initialization values."""
    try:
        # Test with zero bytes (invalid private key)
        with pytest.raises((ValueError, RuntimeError)):
            PrivateKey(b"\x00" * 32)

        # Test with value >= curve order (invalid)
        large_value = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + 1
        with pytest.raises((ValueError, RuntimeError)):
            PrivateKey(large_value)
    except ImportError:
        pytest.skip("curve operations not available")


def test_public_key_verification_invalid_signature():
    """Test public key signature verification with invalid signatures."""
    try:
        priv = PrivateKey()
        pub = priv.public_key()
        message = TEST_MESSAGE_BYTES2

        # Valid signature
        signature = priv.sign(message)
        assert pub.verify(signature, message)

        # Test with invalid signature - these should raise ValueError from DER parsing
        with pytest.raises(ValueError):
            pub.verify(b"invalid", message)
        with pytest.raises(ValueError):
            pub.verify(b"", message)
        with pytest.raises(ValueError):
            pub.verify(b"\x00" * 64, message)
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_public_key_verification_different_message():
    """Test public key signature verification with different message."""
    try:
        priv = PrivateKey()
        pub = priv.public_key()
        message1 = b"test message 1"
        message2 = b"test message 2"

        signature = priv.sign(message1)

        # Should verify for original message but not for different message
        assert pub.verify(signature, message1)
        assert not pub.verify(signature, message2)
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_public_key_verification_wrong_key():
    """Test public key signature verification with wrong key."""
    try:
        priv1 = PrivateKey()
        priv2 = PrivateKey()
        pub2 = priv2.public_key()
        message = TEST_MESSAGE_BYTES2

        signature = priv1.sign(message)

        # Should not verify with wrong public key
        assert not pub2.verify(signature, message)
    except ImportError:
        pytest.skip(SKIP_SIGNATURE_OPS)


def test_private_key_serialize():
    """Test private key serialization."""
    key_bytes = b"\x02" * 32
    priv = PrivateKey(key_bytes)
    assert priv.serialize() == key_bytes


# ========================================================================
# PublicKey initialization branches
# ========================================================================


def test_public_key_from_private():
    """Test creating public key from private key."""
    priv = PrivateKey()
    pub = priv.public_key()
    assert hasattr(pub, "address")


def test_public_key_from_bytes_compressed():
    """Test creating public key from compressed bytes."""
    # Compressed public key (33 bytes starting with 02 or 03)
    pub_bytes = b"\x02" + b"\x00" * 32
    try:
        pub = PublicKey(pub_bytes)
        assert hasattr(pub, "address")
    except Exception:
        # May fail if invalid point
        pass


def test_public_key_from_bytes_uncompressed():
    """Test creating public key from uncompressed bytes."""
    # Uncompressed public key (65 bytes starting with 04)
    pub_bytes = b"\x04" + b"\x00" * 64
    try:
        pub = PublicKey(pub_bytes)
        assert hasattr(pub, "address")
    except Exception:
        # May fail if invalid point
        pass


# ========================================================================
# PublicKey methods
# ========================================================================


def test_public_key_verify_valid():
    """Test public key verify with valid signature."""
    priv = PrivateKey()
    pub = priv.public_key()
    message = TEST_MESSAGE_BYTES
    signature = priv.sign(message)

    is_valid = pub.verify(signature, message)
    assert is_valid


def test_public_key_verify_invalid_signature():
    """Test public key verify with invalid signature."""
    priv = PrivateKey()
    pub = priv.public_key()
    message = TEST_MESSAGE_BYTES

    with pytest.raises(ValueError):
        pub.verify(b"invalid_signature", message)


def test_public_key_verify_wrong_message():
    """Test public key verify with wrong message."""
    priv = PrivateKey()
    pub = priv.public_key()
    message1 = b"message 1"
    message2 = b"message 2"
    signature = priv.sign(message1)

    is_valid = pub.verify(signature, message2)
    assert not is_valid


def test_public_key_to_address():
    """Test public key to address conversion."""
    priv = PrivateKey()
    pub = priv.public_key()
    address = pub.address()
    assert isinstance(address, str)
    assert len(address) > 0


def test_public_key_serialize():
    """Test public key serialization."""
    priv = PrivateKey()
    pub = priv.public_key()
    serialized = pub.serialize()
    assert isinstance(serialized, bytes)
    assert len(serialized) in [33, 65]  # Compressed or uncompressed


# ========================================================================
# Edge cases
# ========================================================================


def test_private_key_deterministic_generation():
    """Test same seed produces same key."""
    key1 = PrivateKey(b"\x01" * 32)
    key2 = PrivateKey(b"\x01" * 32)
    assert key1.serialize() == key2.serialize()


def test_private_key_different_seeds():
    """Test different seeds produce different keys."""
    key1 = PrivateKey(b"\x01" * 32)
    key2 = PrivateKey(b"\x02" * 32)
    assert key1.serialize() != key2.serialize()


def test_public_key_from_same_private():
    """Test same private key produces same public key."""
    priv = PrivateKey(b"\x01" * 32)
    pub1 = priv.public_key()
    pub2 = priv.public_key()
    assert pub1.serialize() == pub2.serialize()
