"""Tests for shared log_integrity HMAC signing and verification."""

from __future__ import annotations

import hashlib
import hmac
import json
from typing import TYPE_CHECKING
from unittest.mock import patch

from python_pkg.shared.log_integrity import (
    _generate_hmac_key,
    _load_hmac_key,
    compute_entry_hmac,
    verify_entry_hmac,
)

if TYPE_CHECKING:
    from pathlib import Path


class TestLoadHmacKey:
    """Tests for _load_hmac_key."""

    def test_loads_key_from_file(self, tmp_path: Path) -> None:
        """Test loading HMAC key from existing file."""
        key_file = tmp_path / "hmac.key"
        key_file.write_bytes(b"secret_key_bytes")
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            result = _load_hmac_key()
        assert result == b"secret_key_bytes"

    def test_returns_none_on_missing_file(self, tmp_path: Path) -> None:
        """Test returns None when key file doesn't exist."""
        key_file = tmp_path / "nonexistent.key"
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            result = _load_hmac_key()
        assert result is None


class TestGenerateHmacKey:
    """Tests for _generate_hmac_key."""

    def test_generates_and_writes_key(self, tmp_path: Path) -> None:
        """Test key generation creates file with 32-byte key."""
        key_file = tmp_path / "subdir" / "hmac.key"
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            result = _generate_hmac_key()
        assert result is not None
        assert len(result) == 32
        assert key_file.read_bytes() == result

    def test_returns_none_on_write_failure(self) -> None:
        """Test returns None when file cannot be written."""
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
        ) as mock_path:
            mock_path.parent.mkdir.side_effect = OSError("permission denied")
            result = _generate_hmac_key()
        assert result is None


class TestComputeEntryHmac:
    """Tests for compute_entry_hmac."""

    def test_computes_hmac_for_entry(self, tmp_path: Path) -> None:
        """Test HMAC computation produces valid hex string."""
        key_file = tmp_path / "hmac.key"
        key = b"test_key_12345"
        key_file.write_bytes(key)
        entry = {"timestamp": "2025-01-01T00:00:00", "workout_data": {"type": "test"}}
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            result = compute_entry_hmac(entry)
        assert result is not None
        # Verify manually
        payload = json.dumps(entry, sort_keys=True, separators=(",", ":"))
        expected = hmac.new(key, payload.encode(), hashlib.sha256).hexdigest()
        assert result == expected

    def test_returns_none_when_no_key(self, tmp_path: Path) -> None:
        """Test returns None when key file is missing."""
        key_file = tmp_path / "nonexistent.key"
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            result = compute_entry_hmac({"data": "test"})
        assert result is None


class TestVerifyEntryHmac:
    """Tests for verify_entry_hmac."""

    def test_valid_hmac(self, tmp_path: Path) -> None:
        """Test verification passes with correct HMAC."""
        key_file = tmp_path / "hmac.key"
        key = b"verification_key"
        key_file.write_bytes(key)
        entry_data = {"timestamp": "2025-01-01", "workout_data": {"type": "test"}}
        payload = json.dumps(entry_data, sort_keys=True, separators=(",", ":"))
        correct_hmac = hmac.new(key, payload.encode(), hashlib.sha256).hexdigest()
        entry = {**entry_data, "hmac": correct_hmac}

        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            assert verify_entry_hmac(entry) is True

    def test_invalid_hmac(self, tmp_path: Path) -> None:
        """Test verification fails with wrong HMAC."""
        key_file = tmp_path / "hmac.key"
        key_file.write_bytes(b"verification_key")
        entry = {"timestamp": "2025-01-01", "hmac": "wrong_hmac_value"}

        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            assert verify_entry_hmac(entry) is False

    def test_missing_hmac_field(self) -> None:
        """Test verification fails when entry has no hmac field."""
        entry: dict[str, object] = {"timestamp": "2025-01-01"}
        assert verify_entry_hmac(entry) is False

    def test_non_string_hmac_field(self) -> None:
        """Test verification fails when hmac field is not a string."""
        entry: dict[str, object] = {"timestamp": "2025-01-01", "hmac": 12345}
        assert verify_entry_hmac(entry) is False

    def test_missing_key_file(self, tmp_path: Path) -> None:
        """Test verification fails when key file doesn't exist."""
        key_file = tmp_path / "nonexistent.key"
        entry = {"timestamp": "2025-01-01", "hmac": "some_hmac"}
        with patch(
            "python_pkg.shared.log_integrity.HMAC_KEY_FILE",
            key_file,
        ):
            assert verify_entry_hmac(entry) is False
