"""Extended tests for database/session_context.py - targeting untested paths.

Covers:
- ensure_db_session() decorator (lines 160-212, entirely untested)
- get_user_db_session() reusing g.db_session
- get_user_db_session() password from g.user_password
- get_user_db_session() password from session_password_store
- get_user_db_session() password from thread context
- get_user_db_session() get_metrics_session returns None
"""

from unittest.mock import Mock, patch

import pytest
from flask import Flask, g, session as flask_session


@pytest.fixture
def app():
    """Create test Flask application."""
    app = Flask(__name__)
    app.config["TESTING"] = True
    app.secret_key = "test-secret-key"
    return app


# ── ensure_db_session decorator ──────────────────────────────────


class TestEnsureDbSession:
    """Tests for ensure_db_session decorator (lines 160-212, entirely untested)."""

    def test_no_username_passes_through(self, app):
        """When no username in session, view runs without db_session setup."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        view_called = []

        @ensure_db_session
        def my_view():
            view_called.append(True)
            return "ok"

        with app.test_request_context():
            result = my_view()

        assert result == "ok"
        assert view_called == [True]

    def test_connected_user_gets_session(self, app):
        """Connected user gets g.db_session set from db_manager."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        mock_session = Mock()

        @ensure_db_session
        def my_view():
            return g.db_session

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.is_user_connected.return_value = True
                mock_db.get_session.return_value = mock_session

                result = my_view()

        assert result is mock_session
        mock_db.get_session.assert_called_once_with("testuser")

    def test_encrypted_db_not_connected_redirects_to_login(self, app):
        """Encrypted DB + disconnected user → clear session + redirect."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        # Register auth blueprint with login route
        from flask import Blueprint

        auth_bp = Blueprint("auth", __name__)

        @auth_bp.route("/login")
        def login():
            return "login page"

        app.register_blueprint(auth_bp)

        @ensure_db_session
        def my_view():
            return "should not reach"

        with app.test_request_context():
            flask_session["username"] = "testuser"
            flask_session["some_data"] = "preserve_check"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.is_user_connected.return_value = False
                mock_db.has_encryption = True

                result = my_view()

        # Should be a redirect response
        assert result.status_code == 302
        assert "/login" in result.location

    def test_unencrypted_db_not_connected_reopens(self, app):
        """Unencrypted DB + disconnected user → reopen database."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
            UNENCRYPTED_DB_PLACEHOLDER,
        )

        mock_session = Mock()

        @ensure_db_session
        def my_view():
            return g.db_session

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.is_user_connected.return_value = False
                mock_db.has_encryption = False
                mock_engine = Mock()
                mock_db.open_user_database.return_value = mock_engine
                mock_db.get_session.return_value = mock_session

                result = my_view()

        mock_db.open_user_database.assert_called_once_with(
            "testuser", UNENCRYPTED_DB_PLACEHOLDER
        )
        assert result is mock_session

    def test_unencrypted_reopen_fails_gracefully(self, app):
        """Failed unencrypted reopen doesn't crash — view still runs."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        @ensure_db_session
        def my_view():
            return "view ran"

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.is_user_connected.return_value = False
                mock_db.has_encryption = False
                mock_db.open_user_database.return_value = None  # Engine failed

                result = my_view()

        assert result == "view ran"

    def test_exception_during_session_setup_returns_500(self, app):
        """Exception in session setup returns 500 error response."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        @ensure_db_session
        def my_view():
            return "recovered"

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.is_user_connected.side_effect = RuntimeError("DB crash")

                result = my_view()

        assert result[1] == 500

    def test_preserves_function_name(self):
        """Decorator preserves wrapped function name via functools.wraps."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        @ensure_db_session
        def my_special_view():
            pass

        assert my_special_view.__name__ == "my_special_view"

    def test_passes_args_to_view(self, app):
        """Decorator passes positional and keyword args to wrapped view."""
        from local_deep_research.database.session_context import (
            ensure_db_session,
        )

        @ensure_db_session
        def my_view(item_id, mode="default"):
            return f"{item_id}:{mode}"

        with app.test_request_context():
            # No username → passes through without DB setup
            result = my_view(42, mode="edit")

        assert result == "42:edit"


# ── get_user_db_session: g.db_session reuse ──────────────────────


class TestGetUserDbSessionReuse:
    """Tests for g.db_session reuse path (lines 62-65)."""

    def test_reuses_existing_g_db_session(self, app):
        """When g.db_session exists, it is reused without creating new session."""
        from local_deep_research.database.session_context import (
            get_user_db_session,
        )

        existing_session = Mock()

        with app.test_request_context():
            flask_session["username"] = "testuser"
            g.db_session = existing_session

            with get_user_db_session() as session:
                assert session is existing_session


# ── get_user_db_session: password resolution paths ───────────────


class TestGetUserDbSessionPasswordPaths:
    """Tests for password resolution fallback chain."""

    def test_password_from_g_user_password(self, app):
        """Password is retrieved from g.user_password."""
        from local_deep_research.database.session_context import (
            get_user_db_session,
        )

        mock_session = Mock()

        with app.test_request_context():
            flask_session["username"] = "testuser"
            g.user_password = "secret123"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.has_encryption = True

                with patch(
                    "local_deep_research.database.thread_local_session.get_metrics_session"
                ) as mock_get:
                    mock_get.return_value = mock_session

                    with get_user_db_session() as session:
                        assert session is mock_session

                    mock_get.assert_called_once_with("testuser", "secret123")

    def test_password_from_session_password_store(self, app):
        """Password is retrieved from session_password_store."""
        from local_deep_research.database.session_context import (
            get_user_db_session,
        )

        mock_session = Mock()

        with app.test_request_context():
            flask_session["username"] = "testuser"
            flask_session["session_id"] = "sess-abc"

            with (
                patch(
                    "local_deep_research.database.session_context.db_manager"
                ) as mock_db,
                patch(
                    "local_deep_research.database.session_context.get_search_context"
                ) as mock_ctx,
            ):
                mock_db.has_encryption = True
                mock_ctx.return_value = None  # No thread context

                # session_password_store is imported locally inside get_user_db_session
                with patch(
                    "local_deep_research.database.session_passwords.session_password_store"
                ) as mock_store:
                    mock_store.get_session_password.return_value = "store-pass"

                    with patch(
                        "local_deep_research.database.thread_local_session.get_metrics_session"
                    ) as mock_get:
                        mock_get.return_value = mock_session

                        with get_user_db_session() as session:
                            assert session is mock_session

                        mock_get.assert_called_once_with(
                            "testuser", "store-pass"
                        )
                        mock_store.get_session_password.assert_called_once_with(
                            "testuser", "sess-abc"
                        )

    def test_password_from_thread_context(self, app):
        """Password is retrieved from thread context (background threads)."""
        from local_deep_research.database.session_context import (
            get_user_db_session,
        )

        mock_session = Mock()

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with (
                patch(
                    "local_deep_research.database.session_context.db_manager"
                ) as mock_db,
                patch(
                    "local_deep_research.database.session_context.get_search_context"
                ) as mock_ctx,
            ):
                mock_db.has_encryption = True
                mock_ctx.return_value = {"user_password": "thread-pass"}

                with patch(
                    "local_deep_research.database.thread_local_session.get_metrics_session"
                ) as mock_get:
                    mock_get.return_value = mock_session

                    with get_user_db_session() as session:
                        assert session is mock_session

                    mock_get.assert_called_once_with("testuser", "thread-pass")

    def test_get_metrics_session_returns_none_raises(self, app):
        """When get_metrics_session returns None, DatabaseSessionError is raised."""
        from local_deep_research.database.session_context import (
            get_user_db_session,
            DatabaseSessionError,
        )

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.has_encryption = False

                with patch(
                    "local_deep_research.database.thread_local_session.get_metrics_session"
                ) as mock_get:
                    mock_get.return_value = None

                    with pytest.raises(
                        DatabaseSessionError,
                        match="Could not establish session",
                    ):
                        with get_user_db_session():
                            pass

    def test_password_stored_in_g_after_success(self, app):
        """After successful session creation, password is stored in g."""
        from local_deep_research.database.session_context import (
            get_user_db_session,
        )

        mock_session = Mock()

        with app.test_request_context():
            flask_session["username"] = "testuser"

            with patch(
                "local_deep_research.database.session_context.db_manager"
            ) as mock_db:
                mock_db.has_encryption = False

                with patch(
                    "local_deep_research.database.thread_local_session.get_metrics_session"
                ) as mock_get:
                    mock_get.return_value = mock_session

                    with get_user_db_session() as _session:
                        # After yield, g.user_password should be set
                        assert hasattr(g, "user_password")
