"""
Extended tests for news/flask_api.py

Tests cover:
- safe_error_message() helper
- get_user_id() helper
- Flask route handlers with mocking
- Error handling and response codes
- Request validation
"""

import pytest
from unittest.mock import patch
from flask import Flask


@pytest.fixture
def flask_app():
    """Create a Flask app for testing."""
    app = Flask(__name__)
    app.config["TESTING"] = True
    return app


class TestSafeErrorMessage:
    """Tests for safe_error_message() helper."""

    def test_returns_string(self):
        """Returns a string."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception("test"))

        assert isinstance(result, str)

    def test_handles_value_error(self):
        """Returns 'Invalid input provided' for ValueError."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(ValueError("bad value"))

        assert result == "Invalid input provided"

    def test_handles_key_error(self):
        """Returns 'Required data missing' for KeyError."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(KeyError("missing_key"))

        assert result == "Required data missing"

    def test_handles_type_error(self):
        """Returns 'Invalid data format' for TypeError."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(TypeError("wrong type"))

        assert result == "Invalid data format"

    def test_handles_generic_exception(self):
        """Returns generic message for other exceptions."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception("unknown error"))

        assert "error occurred" in result.lower()

    def test_includes_context_in_message(self):
        """Includes context in error message."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception("error"), context="testing")

        assert "testing" in result

    def test_handles_none_exception(self):
        """Handles None-like exception gracefully."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception(""))

        assert isinstance(result, str)

    def test_empty_context(self):
        """Handles empty context string."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception("error"), context="")

        assert isinstance(result, str)


class TestGetUserId:
    """Tests for get_user_id() helper."""

    def test_function_exists(self):
        """get_user_id function exists."""
        from local_deep_research.news.flask_api import get_user_id

        assert callable(get_user_id)

    def test_returns_none_when_no_current_user(self):
        """Returns None when current_user returns None."""
        # Create app context for the import inside get_user_id
        app = Flask(__name__)
        with app.app_context():
            with patch(
                "local_deep_research.web.auth.decorators.current_user"
            ) as mock_current:
                mock_current.return_value = None

                from local_deep_research.news.flask_api import get_user_id

                result = get_user_id()

                assert result is None

    def test_returns_username_when_user_exists(self):
        """Returns username when current_user returns a username."""
        app = Flask(__name__)
        with app.app_context():
            with patch(
                "local_deep_research.web.auth.decorators.current_user"
            ) as mock_current:
                mock_current.return_value = "testuser"

                from local_deep_research.news.flask_api import get_user_id

                result = get_user_id()

                assert result == "testuser"


class TestNewsBlueprintExists:
    """Tests for news_api_bp Blueprint."""

    def test_blueprint_is_defined(self):
        """Blueprint news_api_bp is defined."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_blueprint_name(self):
        """Blueprint name is 'news_api'."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp.name == "news_api"

    def test_blueprint_url_prefix(self):
        """Blueprint has /api url_prefix."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp.url_prefix == "/api"


class TestFeedRouteValidation:
    """Tests for /feed route validation."""

    def test_feed_route_function_exists(self):
        """get_news_feed function exists."""
        from local_deep_research.news.flask_api import get_news_feed

        assert callable(get_news_feed)


class TestSubscriptionRouteValidation:
    """Tests for subscription routes."""

    def test_subscribe_route_exists(self):
        """Route /api/subscribe exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_subscriptions_current_route_exists(self):
        """Route /api/subscriptions/current exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestVoteRouteValidation:
    """Tests for vote/feedback routes."""

    def test_vote_route_exists(self):
        """Route /api/vote exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_feedback_batch_route_exists(self):
        """Route /api/feedback/batch exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestSchedulerRoutes:
    """Tests for scheduler management routes."""

    def test_scheduler_status_route_exists(self):
        """Route /api/scheduler/status exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_scheduler_start_route_exists(self):
        """Route /api/scheduler/start exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_scheduler_stop_route_exists(self):
        """Route /api/scheduler/stop exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestFolderRoutes:
    """Tests for folder management routes."""

    def test_get_folders_route_exists(self):
        """Route /api/subscription/folders exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_create_folder_route_exists(self):
        """POST /api/subscription/folders exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestErrorHandlers:
    """Tests for error handler functions."""

    def test_bad_request_handler_exists(self):
        """400 error handler is defined."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import bad_request

            result = bad_request(Exception("test"))

            assert result[1] == 400

    def test_not_found_handler_exists(self):
        """404 error handler is defined."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import not_found

            result = not_found(Exception("test"))

            assert result[1] == 404

    def test_internal_error_handler_exists(self):
        """500 error handler is defined."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import internal_error

            result = internal_error(Exception("test"))

            assert result[1] == 500

    def test_bad_request_returns_json(self):
        """400 error handler returns JSON."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import bad_request

            result = bad_request(Exception("test"))

            # Result is tuple (response, status_code)
            assert "error" in result[0].get_json()

    def test_not_found_returns_json(self):
        """404 error handler returns JSON."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import not_found

            result = not_found(Exception("test"))

            assert "error" in result[0].get_json()

    def test_internal_error_returns_json(self):
        """500 error handler returns JSON."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import internal_error

            result = internal_error(Exception("test"))

            assert "error" in result[0].get_json()


class TestSearchHistoryRoutes:
    """Tests for search history routes."""

    def test_get_search_history_route_exists(self):
        """GET /api/search-history exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_add_search_history_route_exists(self):
        """POST /api/search-history exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_clear_search_history_route_exists(self):
        """DELETE /api/search-history exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestPreferencesRoute:
    """Tests for preferences route."""

    def test_preferences_route_exists(self):
        """POST /api/preferences exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestCategoriesRoute:
    """Tests for categories route."""

    def test_categories_route_exists(self):
        """GET /api/categories exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestResearchRoute:
    """Tests for research route."""

    def test_research_route_exists(self):
        """POST /api/research/<card_id> exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestSubscriptionManagement:
    """Tests for subscription CRUD routes."""

    def test_get_subscription_route_exists(self):
        """GET /api/subscriptions/<id> exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_update_subscription_route_exists(self):
        """PUT /api/subscriptions/<id> exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_delete_subscription_route_exists(self):
        """DELETE /api/subscriptions/<id> exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_run_subscription_route_exists(self):
        """POST /api/subscriptions/<id>/run exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_history_route_exists(self):
        """GET /api/subscriptions/<id>/history exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestOverdueSubscriptions:
    """Tests for overdue subscription checking."""

    def test_check_overdue_route_exists(self):
        """POST /api/check-overdue exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestSubscriptionStats:
    """Tests for subscription stats route."""

    def test_stats_route_exists(self):
        """GET /api/subscription/stats exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestOrganizedSubscriptions:
    """Tests for organized subscriptions route."""

    def test_organized_route_exists(self):
        """GET /api/subscription/subscriptions/organized exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestSchedulerStats:
    """Tests for scheduler stats route."""

    def test_scheduler_stats_route_exists(self):
        """GET /api/scheduler/stats exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None

    def test_scheduler_users_route_exists(self):
        """GET /api/scheduler/users exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestCleanupRoute:
    """Tests for cleanup trigger route."""

    def test_cleanup_route_exists(self):
        """POST /api/scheduler/cleanup-now exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestCheckNowRoute:
    """Tests for check-now route."""

    def test_check_now_route_exists(self):
        """POST /api/scheduler/check-now exists."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp is not None


class TestSafeErrorMessageExtended:
    """Extended tests for safe_error_message() helper."""

    def test_attribute_error_returns_generic(self):
        """Returns generic message for AttributeError."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(AttributeError("missing attr"))

        assert "error occurred" in result.lower()

    def test_index_error_returns_generic(self):
        """Returns generic message for IndexError."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(IndexError("list index out of range"))

        assert "error occurred" in result.lower()

    def test_runtime_error_returns_generic(self):
        """Returns generic message for RuntimeError."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(RuntimeError("runtime issue"))

        assert "error occurred" in result.lower()

    def test_context_with_special_characters(self):
        """Handles context with special characters."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception("error"), context="test <>&\"'")

        assert isinstance(result, str)

    def test_long_context_string(self):
        """Handles long context string."""
        from local_deep_research.news.flask_api import safe_error_message

        long_context = "x" * 1000
        result = safe_error_message(Exception("error"), context=long_context)

        assert isinstance(result, str)

    def test_unicode_context(self):
        """Handles unicode in context."""
        from local_deep_research.news.flask_api import safe_error_message

        result = safe_error_message(Exception("error"), context="日本語")

        assert isinstance(result, str)


class TestBlueprintConfiguration:
    """Tests for Blueprint configuration."""

    def test_blueprint_has_routes_registered(self):
        """Blueprint has routes registered."""
        from local_deep_research.news.flask_api import news_api_bp

        # Blueprint should have deferred functions
        assert len(news_api_bp.deferred_functions) > 0

    def test_blueprint_is_flask_blueprint(self):
        """Blueprint is a Flask Blueprint instance."""
        from local_deep_research.news.flask_api import news_api_bp
        from flask import Blueprint

        assert isinstance(news_api_bp, Blueprint)

    def test_blueprint_import_name(self):
        """Blueprint has correct import_name."""
        from local_deep_research.news.flask_api import news_api_bp

        assert news_api_bp.import_name is not None


class TestErrorHandlerMessages:
    """Tests for error handler message content."""

    def test_bad_request_message_content(self):
        """400 handler returns 'Bad request' message."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import bad_request

            result = bad_request(Exception("test"))
            json_data = result[0].get_json()

            assert json_data["error"] == "Bad request"

    def test_not_found_message_content(self):
        """404 handler returns 'Resource not found' message."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import not_found

            result = not_found(Exception("test"))
            json_data = result[0].get_json()

            assert json_data["error"] == "Resource not found"

    def test_internal_error_message_content(self):
        """500 handler returns 'Internal server error' message."""
        app = Flask(__name__)
        with app.app_context():
            from local_deep_research.news.flask_api import internal_error

            result = internal_error(Exception("test"))
            json_data = result[0].get_json()

            assert json_data["error"] == "Internal server error"
