"""
Tests specifically for the pure Python (native) implementation.

These tests ensure the native Python implementation has equivalent
coverage and correctness as the C implementation. Tests follow FIRST principles.
"""

import pytest

from kheavyhash.kheavyhash_python import kheavyhash

# Import test vectors
# Mypy sees redefinitions here due to try/except fallback pattern - this is intentional
try:
    from tests.test_vectors import (  # type: ignore[no-redef]
        TEST_VECTOR_1_NONCE,
        TEST_VECTOR_1_PRE_POW_HASH,
        TEST_VECTOR_1_TIMESTAMP,
        TEST_VECTORS,
        make_pre_pow_hash_from_bytes,
    )
except ImportError:
    try:
        from test_vectors import (  # type: ignore[no-redef]
            TEST_VECTOR_1_NONCE,
            TEST_VECTOR_1_PRE_POW_HASH,
            TEST_VECTOR_1_TIMESTAMP,
            TEST_VECTORS,
            make_pre_pow_hash_from_bytes,
        )
    except ImportError:
        # Fallback values if test_vectors not available
        # These are intentionally redefined as fallbacks - ignore mypy warnings
        TEST_VECTORS = []  # type: ignore[no-redef]
        TEST_VECTOR_1_PRE_POW_HASH = bytes.fromhex(  # type: ignore[no-redef]
            "0ad86e9bef09726cdc75913e44ec96521391c7ceb2aae3c633f46a94bf4d2546"
        )
        TEST_VECTOR_1_TIMESTAMP = bytes.fromhex("3e6bd36895010000")  # type: ignore[no-redef]
        TEST_VECTOR_1_NONCE = bytes.fromhex("39fd069384065157")  # type: ignore[no-redef]
        import hashlib

        def make_pre_pow_hash_from_bytes(data: bytes) -> bytes:  # type: ignore[no-redef]
            first_hash = hashlib.sha256(data).digest()
            return hashlib.sha256(first_hash).digest()


class TestNativeImplementation:
    """Test the native Python implementation directly."""

    def test_native_basic_functionality(self):
        """Test basic functionality of Python implementation."""
        # Test with known good values from test vectors
        padding = bytes(32)
        nonce = bytes(8)
        test_input = TEST_VECTOR_1_PRE_POW_HASH + TEST_VECTOR_1_TIMESTAMP + padding + nonce

        result = kheavyhash(test_input)
        assert len(result) == 32
        assert isinstance(result, bytes)

        # Test with pattern
        pre_pow_hash2 = bytes(range(32))
        test_input2 = pre_pow_hash2 + TEST_VECTOR_1_TIMESTAMP + padding + nonce
        result2 = kheavyhash(test_input2)
        assert len(result2) == 32
        assert result2 != result  # Different inputs should produce different outputs

    def test_native_deterministic(self):
        """Test that Python implementation produces deterministic results."""
        padding = bytes(32)
        nonce = bytes(8)
        test_input = TEST_VECTOR_1_PRE_POW_HASH + TEST_VECTOR_1_TIMESTAMP + padding + nonce
        result1 = kheavyhash(test_input)
        result2 = kheavyhash(test_input)
        assert result1 == result2, "Python implementation should be deterministic"

    def test_native_input_validation(self):
        """Test that Python implementation validates input length."""
        # Too short
        with pytest.raises(ValueError, match="exactly 80 bytes"):
            kheavyhash(bytes(79))

        # Too long
        with pytest.raises(ValueError, match="exactly 80 bytes"):
            kheavyhash(bytes(81))

    @pytest.mark.parametrize(
        "input_hex,expected_hex", [(tv[0], tv[1]) for tv in TEST_VECTORS if tv[1] is not None]
    )
    def test_native_test_vectors(self, input_hex, expected_hex):
        """
        Test Python implementation against known test vectors from Go reference.

        This ensures the pure Python implementation produces correct results
        for all test vectors, matching the Go reference implementation.
        """
        if expected_hex is None:
            pytest.skip("Test vector not yet populated")

        work_order = bytes.fromhex(input_hex)
        assert len(work_order) == 80

        result = kheavyhash(work_order)
        expected = bytes.fromhex(expected_hex)

        assert len(result) == 32
        assert len(expected) == 32
        assert result == expected, (
            f"Python implementation output mismatch!\n"
            f"Input:    {input_hex}\n"
            f"Expected: {expected_hex}\n"
            f"Got:      {result.hex()}\n"
            f"This indicates the Python implementation does not match the Go reference."
        )

    def test_native_different_inputs_produce_different_outputs(self):
        """Test that different inputs produce different outputs."""
        padding = bytes(32)
        nonce = bytes(8)
        input1 = TEST_VECTOR_1_PRE_POW_HASH + TEST_VECTOR_1_TIMESTAMP + padding + nonce

        # Different pre_pow_hash but same timestamp
        pre_pow_hash2 = bytes([1] + [0] * 31)
        input2 = pre_pow_hash2 + TEST_VECTOR_1_TIMESTAMP + padding + nonce

        result1 = kheavyhash(input1)
        result2 = kheavyhash(input2)
        assert result1 != result2

    def test_native_matrix_full_rank_requirement(self):
        """
        Test that the Python implementation successfully generates full-rank matrices.

        This test verifies that the matrix generation and rank checking works correctly
        even for inputs that may require many attempts.
        """
        # Use various inputs
        padding = bytes(32)
        nonce = bytes(8)
        test_cases = [
            bytes(range(32)) + TEST_VECTOR_1_TIMESTAMP + padding + nonce,
            bytes([0xFF] * 32) + TEST_VECTOR_1_TIMESTAMP + padding + nonce,
            bytes([i % 256 for i in range(32)]) + TEST_VECTOR_1_TIMESTAMP + padding + nonce,
        ]

        for test_input in test_cases:
            # Should not raise RuntimeError about failing to generate full-rank matrix
            result = kheavyhash(test_input)
            assert len(result) == 32

    def test_native_work_order_structure(self):
        """Test that work order structure is correctly interpreted."""
        # Create a work order with distinct values in each section
        pre_pow_hash = bytes([0x11] * 32)
        timestamp = bytes([0x22] * 8)
        padding = bytes([0x33] * 32)
        nonce = bytes([0x44] * 8)

        work_order = pre_pow_hash + timestamp + padding + nonce
        result = kheavyhash(work_order)

        # Verify each section is used (result should differ if we change any section)
        work_order2 = (
            bytes([0x11] * 32) + bytes([0x22] * 8) + bytes([0x33] * 32) + bytes([0x55] * 8)
        )
        result2 = kheavyhash(work_order2)
        assert result != result2  # Different nonce should produce different result


class TestNativeVsCImplementation:
    """Test that native and C implementations produce same results."""

    def test_implementations_consistent_basic(self):
        """Test that both implementations produce same results for basic inputs."""
        try:
            from kheavyhash._c_ext import kheavyhash as c_kheavyhash
        except ImportError:
            pytest.skip("C extension not available, cannot compare")

        # Use known good values from test vectors
        padding = bytes(32)
        nonce = bytes(8)
        test_input1 = TEST_VECTOR_1_PRE_POW_HASH + TEST_VECTOR_1_TIMESTAMP + padding + nonce

        # Create additional test cases
        pre_pow_hash2 = bytes(range(32))
        test_input2 = pre_pow_hash2 + TEST_VECTOR_1_TIMESTAMP + padding + nonce

        # All ones with known good timestamp
        pre_pow_hash3 = bytes([0xFF] * 32)
        test_input3 = pre_pow_hash3 + TEST_VECTOR_1_TIMESTAMP + padding + nonce

        test_cases = [
            test_input1,  # Known good test vector values
            test_input2,  # Pattern with known good timestamp
            test_input3,  # All ones with known good timestamp
        ]

        for test_input in test_cases:
            native_result = kheavyhash(test_input)
            c_result = c_kheavyhash(test_input)
            assert native_result == c_result, (
                f"Implementations differ for input: {test_input.hex()[:32]}...\n"
                f"Native: {native_result.hex()}\n"
                f"C:      {c_result.hex()}"
            )

    @pytest.mark.parametrize("input_hex", [tv[0] for tv in TEST_VECTORS])
    def test_implementations_consistent_vectors(self, input_hex):
        """Test that both implementations produce same results for test vectors."""
        try:
            from kheavyhash._c_ext import kheavyhash as c_kheavyhash
        except ImportError:
            pytest.skip("C extension not available, cannot compare")

        work_order = bytes.fromhex(input_hex)
        assert len(work_order) == 80

        native_result = kheavyhash(work_order)
        c_result = c_kheavyhash(work_order)

        assert native_result == c_result, (
            f"Implementations differ for test vector: {input_hex[:32]}...\n"
            f"Native: {native_result.hex()}\n"
            f"C:      {c_result.hex()}"
        )
