"""
Tests for FAISS Index Verifier - File integrity verification for FAISS vector indexes.

Tests cover:
- File type detection (should_verify)
- FAISS-specific policy enforcement
- Checksum calculation
- Edge cases and error handling
"""

import hashlib
import tempfile
from pathlib import Path

import pytest

from local_deep_research.security.file_integrity.verifiers.faiss_verifier import (
    FAISSIndexVerifier,
)
from local_deep_research.security.file_integrity.base_verifier import FileType


@pytest.fixture
def verifier():
    """Create a FAISS verifier instance."""
    return FAISSIndexVerifier()


@pytest.fixture
def temp_faiss_file():
    """Create a temporary .faiss file."""
    with tempfile.NamedTemporaryFile(suffix=".faiss", delete=False) as f:
        f.write(b"fake faiss index content")
        return Path(f.name)


@pytest.fixture
def temp_non_faiss_file():
    """Create a temporary non-.faiss file."""
    with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as f:
        f.write(b"text content")
        return Path(f.name)


class TestFAISSVerifierInitialization:
    """Tests for FAISS verifier initialization."""

    def test_verifier_creates_successfully(self):
        """Verifier can be instantiated."""
        verifier = FAISSIndexVerifier()
        assert verifier is not None

    def test_verifier_inherits_base_verifier(self):
        """Verifier inherits from BaseFileVerifier."""
        from local_deep_research.security.file_integrity.base_verifier import (
            BaseFileVerifier,
        )

        verifier = FAISSIndexVerifier()
        assert isinstance(verifier, BaseFileVerifier)


class TestShouldVerify:
    """Tests for should_verify method."""

    def test_should_verify_faiss_extension(self, verifier):
        """Should return True for .faiss files."""
        path = Path("/data/indexes/my_index.faiss")
        assert verifier.should_verify(path) is True

    def test_should_verify_faiss_uppercase_extension(self, verifier):
        """Should return True for .FAISS uppercase extension."""
        path = Path("/data/indexes/my_index.FAISS")
        assert verifier.should_verify(path) is True

    def test_should_verify_faiss_mixed_case_extension(self, verifier):
        """Should return True for .Faiss mixed case extension."""
        path = Path("/data/indexes/my_index.Faiss")
        assert verifier.should_verify(path) is True

    def test_should_not_verify_pdf_extension(self, verifier):
        """Should return False for .pdf files."""
        path = Path("/data/docs/document.pdf")
        assert verifier.should_verify(path) is False

    def test_should_not_verify_txt_extension(self, verifier):
        """Should return False for .txt files."""
        path = Path("/data/notes.txt")
        assert verifier.should_verify(path) is False

    def test_should_not_verify_no_extension(self, verifier):
        """Should return False for files without extension."""
        path = Path("/data/file_without_extension")
        assert verifier.should_verify(path) is False

    def test_should_not_verify_faiss_in_name(self, verifier):
        """Should return False for files with 'faiss' in name but different extension."""
        path = Path("/data/faiss_backup.json")
        assert verifier.should_verify(path) is False

    def test_should_verify_nested_directory_path(self, verifier):
        """Should return True for .faiss files in nested directories."""
        path = Path("/data/embeddings/user123/collection_abc/index.faiss")
        assert verifier.should_verify(path) is True

    def test_should_verify_hidden_file(self, verifier):
        """Should return True for hidden .faiss files."""
        path = Path("/data/.hidden_index.faiss")
        assert verifier.should_verify(path) is True

    def test_should_not_verify_empty_path(self, verifier):
        """Should handle empty file name gracefully."""
        path = Path("")
        # Empty path has no suffix
        assert verifier.should_verify(path) is False


class TestGetFileType:
    """Tests for get_file_type method."""

    def test_returns_faiss_index_type(self, verifier):
        """Should return FileType.FAISS_INDEX."""
        assert verifier.get_file_type() == FileType.FAISS_INDEX

    def test_file_type_is_enum_member(self, verifier):
        """File type should be a valid FileType enum member."""
        file_type = verifier.get_file_type()
        assert isinstance(file_type, FileType)

    def test_file_type_value_is_string(self, verifier):
        """File type value should be a string."""
        file_type = verifier.get_file_type()
        assert file_type.value == "faiss_index"


class TestAllowsModifications:
    """Tests for allows_modifications method."""

    def test_does_not_allow_modifications(self, verifier):
        """FAISS indexes should never allow modifications."""
        assert verifier.allows_modifications() is False

    def test_modification_policy_is_strict(self, verifier):
        """Modification policy should be consistently False."""
        # Multiple calls should return same result
        for _ in range(10):
            assert verifier.allows_modifications() is False


class TestChecksumCalculation:
    """Tests for checksum calculation (inherited from BaseFileVerifier)."""

    def test_calculate_checksum_returns_hex_string(
        self, verifier, temp_faiss_file
    ):
        """Checksum should be a hex string."""
        checksum = verifier.calculate_checksum(temp_faiss_file)
        assert isinstance(checksum, str)
        # SHA256 produces 64 hex characters
        assert len(checksum) == 64
        # All characters should be valid hex
        assert all(c in "0123456789abcdef" for c in checksum)

    def test_calculate_checksum_consistency(self, verifier, temp_faiss_file):
        """Same file should produce same checksum."""
        checksum1 = verifier.calculate_checksum(temp_faiss_file)
        checksum2 = verifier.calculate_checksum(temp_faiss_file)
        assert checksum1 == checksum2

    def test_calculate_checksum_different_files(self, verifier):
        """Different files should produce different checksums."""
        with tempfile.NamedTemporaryFile(suffix=".faiss", delete=False) as f1:
            f1.write(b"content A")
            path1 = Path(f1.name)

        with tempfile.NamedTemporaryFile(suffix=".faiss", delete=False) as f2:
            f2.write(b"content B")
            path2 = Path(f2.name)

        checksum1 = verifier.calculate_checksum(path1)
        checksum2 = verifier.calculate_checksum(path2)
        assert checksum1 != checksum2

    def test_calculate_checksum_file_not_found(self, verifier):
        """Should raise FileNotFoundError for non-existent file."""
        path = Path("/nonexistent/path/index.faiss")
        with pytest.raises(FileNotFoundError):
            verifier.calculate_checksum(path)

    def test_calculate_checksum_large_file(self, verifier):
        """Should handle large files (chunked reading)."""
        with tempfile.NamedTemporaryFile(suffix=".faiss", delete=False) as f:
            # Write 1MB of data
            f.write(b"x" * (1024 * 1024))
            path = Path(f.name)

        checksum = verifier.calculate_checksum(path)
        assert len(checksum) == 64

    def test_calculate_checksum_empty_file(self, verifier):
        """Should handle empty files."""
        with tempfile.NamedTemporaryFile(suffix=".faiss", delete=False) as f:
            # Write nothing - empty file
            path = Path(f.name)

        checksum = verifier.calculate_checksum(path)
        # SHA256 of empty string
        expected = hashlib.sha256(b"").hexdigest()
        assert checksum == expected


class TestGetAlgorithm:
    """Tests for get_algorithm method (inherited from BaseFileVerifier)."""

    def test_get_algorithm_returns_sha256(self, verifier):
        """Should return sha256 as the algorithm."""
        assert verifier.get_algorithm() == "sha256"


class TestEdgeCases:
    """Edge case tests for FAISS verifier."""

    def test_path_with_special_characters(self, verifier):
        """Should handle paths with special characters."""
        path = Path("/data/user's files/my (index) [v2].faiss")
        assert verifier.should_verify(path) is True

    def test_path_with_unicode_characters(self, verifier):
        """Should handle paths with unicode characters."""
        path = Path("/données/索引/インデックス.faiss")
        assert verifier.should_verify(path) is True

    def test_path_with_spaces(self, verifier):
        """Should handle paths with spaces."""
        path = Path("/my documents/vector indexes/main index.faiss")
        assert verifier.should_verify(path) is True

    def test_very_long_path(self, verifier):
        """Should handle very long paths."""
        long_dir = "a" * 200
        path = Path(f"/{long_dir}/index.faiss")
        assert verifier.should_verify(path) is True

    def test_relative_path(self, verifier):
        """Should handle relative paths."""
        path = Path("./relative/path/index.faiss")
        assert verifier.should_verify(path) is True

    def test_multiple_extensions(self, verifier):
        """Should only check final extension."""
        path = Path("/data/index.backup.faiss")
        assert verifier.should_verify(path) is True

        path_wrong = Path("/data/index.faiss.backup")
        assert verifier.should_verify(path_wrong) is False


class TestIntegrationWithIntegrityManager:
    """Tests for integration with the integrity manager."""

    def test_verifier_can_be_registered(self, verifier):
        """Verifier should have all methods required for registration."""
        # Check all required methods exist
        assert hasattr(verifier, "should_verify")
        assert hasattr(verifier, "get_file_type")
        assert hasattr(verifier, "allows_modifications")
        assert hasattr(verifier, "calculate_checksum")
        assert hasattr(verifier, "get_algorithm")

    def test_verifier_methods_are_callable(self, verifier):
        """All verifier methods should be callable."""
        assert callable(verifier.should_verify)
        assert callable(verifier.get_file_type)
        assert callable(verifier.allows_modifications)
        assert callable(verifier.calculate_checksum)
        assert callable(verifier.get_algorithm)
