"""
Deep behavioral tests for safe_error_message and flask_api helpers.
Tests exception type mapping, context formatting, and field mapping patterns.
"""

from local_deep_research.news.flask_api import safe_error_message


# --- Exception type mapping ---


class TestSafeErrorMessageTypes:
    """Tests for exception type → message mapping."""

    def test_value_error_returns_invalid_input(self):
        result = safe_error_message(ValueError("bad value"), "test")
        assert result == "Invalid input provided"

    def test_key_error_returns_required_data_missing(self):
        result = safe_error_message(KeyError("missing_key"), "test")
        assert result == "Required data missing"

    def test_type_error_returns_invalid_data_format(self):
        result = safe_error_message(TypeError("wrong type"), "test")
        assert result == "Invalid data format"

    def test_runtime_error_returns_generic(self):
        result = safe_error_message(RuntimeError("oops"), "test")
        assert "An error occurred" in result

    def test_exception_returns_generic(self):
        result = safe_error_message(Exception("generic"), "test")
        assert "An error occurred" in result

    def test_os_error_returns_generic(self):
        result = safe_error_message(OSError("disk full"), "test")
        assert "An error occurred" in result

    def test_attribute_error_returns_generic(self):
        result = safe_error_message(AttributeError("no attr"), "test")
        assert "An error occurred" in result

    def test_index_error_returns_generic(self):
        result = safe_error_message(IndexError("out of range"), "test")
        assert "An error occurred" in result


# --- Context formatting ---


class TestSafeErrorMessageContext:
    """Tests for context string formatting."""

    def test_context_included_in_generic_message(self):
        result = safe_error_message(Exception("err"), "getting news feed")
        assert "getting news feed" in result

    def test_context_with_while(self):
        result = safe_error_message(Exception("err"), "loading data")
        assert "while loading data" in result

    def test_empty_context(self):
        result = safe_error_message(Exception("err"), "")
        assert "An error occurred" in result
        assert "while" not in result

    def test_no_context(self):
        result = safe_error_message(Exception("err"))
        assert "An error occurred" in result

    def test_context_not_in_value_error(self):
        """ValueError messages should not include context."""
        result = safe_error_message(ValueError("err"), "loading data")
        assert result == "Invalid input provided"

    def test_context_not_in_key_error(self):
        """KeyError messages should not include context."""
        result = safe_error_message(KeyError("err"), "loading data")
        assert result == "Required data missing"

    def test_context_not_in_type_error(self):
        """TypeError messages should not include context."""
        result = safe_error_message(TypeError("err"), "loading data")
        assert result == "Invalid data format"


# --- Security: no internal details leaked ---


class TestSafeErrorMessageSecurity:
    """Tests that internal error details are not exposed."""

    def test_value_error_hides_message(self):
        result = safe_error_message(ValueError("secret_db_password"))
        assert "secret_db_password" not in result

    def test_key_error_hides_message(self):
        result = safe_error_message(KeyError("internal_column_name"))
        assert "internal_column_name" not in result

    def test_type_error_hides_message(self):
        result = safe_error_message(TypeError("NoneType has no attribute foo"))
        assert "NoneType" not in result

    def test_generic_error_hides_stack_trace(self):
        result = safe_error_message(
            RuntimeError("Traceback: at line 42 in secret.py")
        )
        assert "Traceback" not in result
        assert "secret.py" not in result

    def test_generic_error_hides_exception_message(self):
        result = safe_error_message(
            RuntimeError("Connection refused at 192.168.1.1:5432")
        )
        assert "192.168.1.1" not in result
        assert "5432" not in result


# --- Flask API field mapping ---


class TestFlaskApiFieldMapping:
    """Tests for the field mapping dictionary used in update_subscription."""

    def test_field_mapping_keys(self):
        """Verify the expected request fields are mapped."""
        # This tests the pattern used at flask_api.py:433-446
        field_mapping = {
            "query": "query_or_topic",
            "name": "name",
            "refresh_minutes": "refresh_interval_minutes",
            "is_active": "is_active",
            "folder_id": "folder_id",
            "model_provider": "model_provider",
            "model": "model",
            "search_strategy": "search_strategy",
            "custom_endpoint": "custom_endpoint",
            "search_engine": "search_engine",
            "search_iterations": "search_iterations",
            "questions_per_iteration": "questions_per_iteration",
        }
        assert "query" in field_mapping
        assert field_mapping["query"] == "query_or_topic"

    def test_field_mapping_refresh_minutes(self):
        field_mapping = {
            "refresh_minutes": "refresh_interval_minutes",
        }
        data = {"refresh_minutes": 30}
        update_data = {}
        for req_field, storage_field in field_mapping.items():
            if req_field in data:
                update_data[storage_field] = data[req_field]
        assert update_data["refresh_interval_minutes"] == 30

    def test_field_mapping_only_present_keys(self):
        field_mapping = {
            "query": "query_or_topic",
            "name": "name",
            "refresh_minutes": "refresh_interval_minutes",
        }
        data = {"name": "My Sub"}
        update_data = {}
        for req_field, storage_field in field_mapping.items():
            if req_field in data:
                update_data[storage_field] = data[req_field]
        assert "name" in update_data
        assert "query_or_topic" not in update_data
        assert "refresh_interval_minutes" not in update_data

    def test_field_mapping_all_fields(self):
        field_mapping = {
            "query": "query_or_topic",
            "name": "name",
            "refresh_minutes": "refresh_interval_minutes",
            "is_active": "is_active",
        }
        data = {
            "query": "AI news",
            "name": "My AI Feed",
            "refresh_minutes": 60,
            "is_active": True,
        }
        update_data = {}
        for req_field, storage_field in field_mapping.items():
            if req_field in data:
                update_data[storage_field] = data[req_field]
        assert update_data["query_or_topic"] == "AI news"
        assert update_data["name"] == "My AI Feed"
        assert update_data["refresh_interval_minutes"] == 60
        assert update_data["is_active"] is True


# --- Subscription ID validation pattern ---


class TestSubscriptionIdValidation:
    """Tests for the subscription ID validation pattern from flask_api."""

    def test_null_string_is_invalid(self):
        subscription_id = "null"
        assert (
            subscription_id == "null"
            or subscription_id == "undefined"
            or not subscription_id
        )

    def test_undefined_string_is_invalid(self):
        subscription_id = "undefined"
        is_invalid = (
            subscription_id == "null"
            or subscription_id == "undefined"
            or not subscription_id
        )
        assert is_invalid

    def test_empty_string_is_invalid(self):
        subscription_id = ""
        is_invalid = (
            subscription_id == "null"
            or subscription_id == "undefined"
            or not subscription_id
        )
        assert is_invalid

    def test_valid_uuid_is_valid(self):
        subscription_id = "abc-123-def-456"
        is_invalid = (
            subscription_id == "null"
            or subscription_id == "undefined"
            or not subscription_id
        )
        assert not is_invalid

    def test_numeric_id_is_valid(self):
        subscription_id = "12345"
        is_invalid = (
            subscription_id == "null"
            or subscription_id == "undefined"
            or not subscription_id
        )
        assert not is_invalid


# --- Vote validation pattern ---


class TestVoteValidation:
    """Tests for the vote validation pattern."""

    def test_up_is_valid(self):
        vote = "up"
        assert vote in ["up", "down"]

    def test_down_is_valid(self):
        vote = "down"
        assert vote in ["up", "down"]

    def test_sideways_is_invalid(self):
        vote = "sideways"
        assert vote not in ["up", "down"]

    def test_empty_is_invalid(self):
        vote = ""
        assert vote not in ["up", "down"]

    def test_none_is_invalid(self):
        vote = None
        assert vote not in ["up", "down"]

    def test_uppercase_up_is_invalid(self):
        vote = "Up"
        assert vote not in ["up", "down"]

    def test_numeric_is_invalid(self):
        vote = 1
        assert vote not in ["up", "down"]
