"""
Comprehensive tests for security/file_write_verifier.py

Tests cover:
- _sanitize_sensitive_data function
- write_file_verified function
- write_json_verified function
- FileWriteSecurityError exception
"""

import json
from pathlib import Path
from unittest.mock import patch

import pytest


class TestSanitizeSensitiveData:
    """Tests for the _sanitize_sensitive_data function."""

    def test_sanitizes_password_key(self):
        """Test that password keys are redacted."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {"username": "admin", "password": "secret123"}
        result = _sanitize_sensitive_data(data)

        assert result["username"] == "admin"
        assert result["password"] == "[REDACTED]"

    def test_sanitizes_api_key(self):
        """Test that api_key is redacted."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {"api_key": "sk-123456", "name": "test"}
        result = _sanitize_sensitive_data(data)

        assert result["api_key"] == "[REDACTED]"
        assert result["name"] == "test"

    def test_sanitizes_various_sensitive_keys(self):
        """Test that all sensitive key variations are redacted."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {
            "apikey": "value1",
            "api-key": "value2",
            "secret": "value3",
            "secret_key": "value4",
            "token": "value5",
            "access_token": "value6",
            "refresh_token": "value7",
            "private_key": "value8",
            "credentials": "value9",
            "auth": "value10",
            "authorization": "value11",
        }
        result = _sanitize_sensitive_data(data)

        for key in data:
            assert result[key] == "[REDACTED]"

    def test_case_insensitive_matching(self):
        """Test that matching is case insensitive."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {"PASSWORD": "secret", "Api_Key": "key123", "TOKEN": "tok"}
        result = _sanitize_sensitive_data(data)

        assert result["PASSWORD"] == "[REDACTED]"
        assert result["Api_Key"] == "[REDACTED]"
        assert result["TOKEN"] == "[REDACTED]"

    def test_sanitizes_nested_dicts(self):
        """Test that nested dictionaries are sanitized."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {
            "config": {
                "api_key": "secret",
                "settings": {"password": "hidden", "name": "test"},
            }
        }
        result = _sanitize_sensitive_data(data)

        assert result["config"]["api_key"] == "[REDACTED]"
        assert result["config"]["settings"]["password"] == "[REDACTED]"
        assert result["config"]["settings"]["name"] == "test"

    def test_sanitizes_lists_of_dicts(self):
        """Test that lists containing dicts are sanitized."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {
            "users": [
                {"name": "user1", "password": "pass1"},
                {"name": "user2", "password": "pass2"},
            ]
        }
        result = _sanitize_sensitive_data(data)

        assert result["users"][0]["password"] == "[REDACTED]"
        assert result["users"][1]["password"] == "[REDACTED]"
        assert result["users"][0]["name"] == "user1"

    def test_preserves_non_sensitive_data(self):
        """Test that non-sensitive data is preserved."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {"name": "test", "value": 123, "flag": True, "items": [1, 2, 3]}
        result = _sanitize_sensitive_data(data)

        assert result == data

    def test_handles_primitive_values(self):
        """Test that primitive values pass through unchanged."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        assert _sanitize_sensitive_data("string") == "string"
        assert _sanitize_sensitive_data(123) == 123
        assert _sanitize_sensitive_data(True) is True
        assert _sanitize_sensitive_data(None) is None

    def test_handles_list_of_primitives(self):
        """Test that lists of primitives pass through."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = [1, 2, "test", True]
        result = _sanitize_sensitive_data(data)

        assert result == data

    def test_handles_non_string_keys(self):
        """Test handling of non-string dictionary keys."""
        from local_deep_research.security.file_write_verifier import (
            _sanitize_sensitive_data,
        )

        data = {1: "value1", "password": "secret"}
        result = _sanitize_sensitive_data(data)

        assert result[1] == "value1"
        assert result["password"] == "[REDACTED]"


class TestFileWriteSecurityError:
    """Tests for the FileWriteSecurityError exception."""

    def test_exception_can_be_raised(self):
        """Test that the exception can be raised and caught."""
        from local_deep_research.security.file_write_verifier import (
            FileWriteSecurityError,
        )

        with pytest.raises(FileWriteSecurityError) as exc_info:
            raise FileWriteSecurityError("Test error message")

        assert "Test error message" in str(exc_info.value)

    def test_exception_inherits_from_exception(self):
        """Test that FileWriteSecurityError inherits from Exception."""
        from local_deep_research.security.file_write_verifier import (
            FileWriteSecurityError,
        )

        assert issubclass(FileWriteSecurityError, Exception)


class TestWriteFileVerified:
    """Tests for the write_file_verified function."""

    @pytest.fixture
    def mock_get_setting(self):
        """Fixture to mock the get_setting_from_snapshot function."""
        with patch(
            "local_deep_research.config.search_config.get_setting_from_snapshot"
        ) as mock:
            yield mock

    def test_writes_file_when_setting_matches(self, tmp_path, mock_get_setting):
        """Test that file is written when setting matches required value."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
        )

        filepath = tmp_path / "test.txt"
        content = "test content"

        mock_get_setting.return_value = True

        write_file_verified(
            filepath, content, "test.setting", required_value=True
        )

        assert filepath.exists()
        assert filepath.read_text() == content

    def test_raises_error_when_setting_mismatch(
        self, tmp_path, mock_get_setting
    ):
        """Test that error is raised when setting doesn't match."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
            FileWriteSecurityError,
        )

        filepath = tmp_path / "test.txt"
        mock_get_setting.return_value = False

        with pytest.raises(FileWriteSecurityError) as exc_info:
            write_file_verified(
                filepath,
                "content",
                "test.setting",
                required_value=True,
                context="test operation",
            )

        assert "test operation" in str(exc_info.value)
        assert "test.setting=True" in str(exc_info.value)

    def test_raises_error_when_setting_not_found(
        self, tmp_path, mock_get_setting
    ):
        """Test that error is raised when setting doesn't exist."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
            FileWriteSecurityError,
        )

        filepath = tmp_path / "test.txt"
        mock_get_setting.side_effect = KeyError("Setting not found")

        with pytest.raises(FileWriteSecurityError):
            write_file_verified(filepath, "content", "nonexistent.setting")

    def test_writes_binary_file(self, tmp_path, mock_get_setting):
        """Test writing binary content."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
        )

        filepath = tmp_path / "test.bin"
        content = b"\x00\x01\x02\x03"

        mock_get_setting.return_value = True

        write_file_verified(filepath, content, "test.setting", mode="wb")

        assert filepath.exists()
        assert filepath.read_bytes() == content

    def test_uses_custom_encoding(self, tmp_path, mock_get_setting):
        """Test writing with custom encoding."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
        )

        filepath = tmp_path / "test.txt"
        content = "日本語テスト"

        mock_get_setting.return_value = True

        write_file_verified(filepath, content, "test.setting", encoding="utf-8")

        assert filepath.exists()
        assert filepath.read_text(encoding="utf-8") == content

    def test_passes_settings_snapshot(self, tmp_path, mock_get_setting):
        """Test that settings_snapshot is passed to get_setting_from_snapshot."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
        )

        filepath = tmp_path / "test.txt"
        snapshot = {"test.setting": True}

        mock_get_setting.return_value = True

        write_file_verified(
            filepath, "content", "test.setting", settings_snapshot=snapshot
        )

        mock_get_setting.assert_called_once_with(
            "test.setting", settings_snapshot=snapshot
        )

    def test_accepts_path_object(self, tmp_path, mock_get_setting):
        """Test that Path objects are accepted."""
        from local_deep_research.security.file_write_verifier import (
            write_file_verified,
        )

        filepath = Path(tmp_path) / "test.txt"

        mock_get_setting.return_value = True

        write_file_verified(filepath, "content", "test.setting")

        assert filepath.exists()


class TestWriteJsonVerified:
    """Tests for the write_json_verified function."""

    @pytest.fixture
    def mock_get_setting(self):
        """Fixture to mock the get_setting_from_snapshot function."""
        with patch(
            "local_deep_research.config.search_config.get_setting_from_snapshot"
        ) as mock:
            yield mock

    def test_writes_json_when_setting_matches(self, tmp_path, mock_get_setting):
        """Test that JSON is written when setting matches."""
        from local_deep_research.security.file_write_verifier import (
            write_json_verified,
        )

        filepath = tmp_path / "test.json"
        data = {"key": "value", "number": 42}

        mock_get_setting.return_value = True

        write_json_verified(filepath, data, "test.setting")

        assert filepath.exists()
        written_data = json.loads(filepath.read_text())
        assert written_data == data

    def test_sanitizes_sensitive_data(self, tmp_path, mock_get_setting):
        """Test that sensitive data is sanitized before writing."""
        from local_deep_research.security.file_write_verifier import (
            write_json_verified,
        )

        filepath = tmp_path / "test.json"
        data = {"name": "test", "password": "secret123", "api_key": "key456"}

        mock_get_setting.return_value = True

        write_json_verified(filepath, data, "test.setting")

        written_data = json.loads(filepath.read_text())
        assert written_data["name"] == "test"
        assert written_data["password"] == "[REDACTED]"
        assert written_data["api_key"] == "[REDACTED]"

    def test_uses_default_indent(self, tmp_path, mock_get_setting):
        """Test that default indent of 2 is used."""
        from local_deep_research.security.file_write_verifier import (
            write_json_verified,
        )

        filepath = tmp_path / "test.json"
        data = {"key": "value"}

        mock_get_setting.return_value = True

        write_json_verified(filepath, data, "test.setting")

        content = filepath.read_text()
        # With indent=2, there should be newlines and spaces
        assert "\n" in content
        assert "  " in content

    def test_custom_json_kwargs(self, tmp_path, mock_get_setting):
        """Test that custom JSON kwargs are passed through."""
        from local_deep_research.security.file_write_verifier import (
            write_json_verified,
        )

        filepath = tmp_path / "test.json"
        data = {"b": 2, "a": 1}

        mock_get_setting.return_value = True

        write_json_verified(
            filepath, data, "test.setting", sort_keys=True, indent=4
        )

        content = filepath.read_text()
        # With sort_keys, 'a' should come before 'b'
        assert content.index('"a"') < content.index('"b"')
        # With indent=4, should have 4 spaces
        assert "    " in content

    def test_writes_list_data(self, tmp_path, mock_get_setting):
        """Test writing list data."""
        from local_deep_research.security.file_write_verifier import (
            write_json_verified,
        )

        filepath = tmp_path / "test.json"
        data = [{"name": "item1"}, {"name": "item2", "password": "secret"}]

        mock_get_setting.return_value = True

        write_json_verified(filepath, data, "test.setting")

        written_data = json.loads(filepath.read_text())
        assert len(written_data) == 2
        assert written_data[1]["password"] == "[REDACTED]"

    def test_raises_error_when_setting_mismatch(
        self, tmp_path, mock_get_setting
    ):
        """Test that error is raised when setting doesn't match."""
        from local_deep_research.security.file_write_verifier import (
            write_json_verified,
            FileWriteSecurityError,
        )

        filepath = tmp_path / "test.json"

        mock_get_setting.return_value = False

        with pytest.raises(FileWriteSecurityError):
            write_json_verified(filepath, {"key": "value"}, "test.setting")


class TestSensitiveKeys:
    """Tests for SENSITIVE_KEYS constant."""

    def test_sensitive_keys_is_frozenset(self):
        """Test that SENSITIVE_KEYS is a frozenset."""
        from local_deep_research.security.file_write_verifier import (
            SENSITIVE_KEYS,
        )

        assert isinstance(SENSITIVE_KEYS, frozenset)

    def test_sensitive_keys_contains_expected_values(self):
        """Test that SENSITIVE_KEYS contains expected sensitive key names."""
        from local_deep_research.security.file_write_verifier import (
            SENSITIVE_KEYS,
        )

        expected_keys = {
            "password",
            "api_key",
            "secret",
            "token",
            "credentials",
        }

        for key in expected_keys:
            assert key in SENSITIVE_KEYS
