"""
Test authentication decorators and middleware.
"""

import pytest
from flask import Flask, g, session

from src.local_deep_research.web.auth.decorators import (
    current_user,
    inject_current_user,
    login_required,
)


@pytest.fixture
def app():
    """Create a minimal Flask app for testing decorators."""
    app = Flask(__name__)
    app.config["SECRET_KEY"] = "test-secret-key"
    app.config["TESTING"] = True
    app.config["WTF_CSRF_ENABLED"] = False

    # Create a minimal auth blueprint for testing
    from flask import Blueprint

    auth_bp = Blueprint("auth", __name__, url_prefix="/auth")

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

    app.register_blueprint(auth_bp)

    # Add test routes
    @app.route("/protected")
    @login_required
    def protected():
        return "Protected content"

    @app.route("/public")
    def public():
        return "Public content"

    @app.route("/user-info")
    @login_required
    def user_info():
        return f"User: {current_user()}"

    # Register before_request handler
    app.before_request(inject_current_user)

    return app


@pytest.fixture
def client(app):
    """Create a test client."""
    return app.test_client()


class TestAuthDecorators:
    """Test authentication decorators."""

    def test_login_required_redirects(self, client):
        """Test that login_required redirects unauthenticated users."""
        response = client.get("/protected")
        assert response.status_code == 302
        assert "/auth/login" in response.location
        # Check that next parameter is set (URL encoded)
        assert "next=" in response.location
        assert "protected" in response.location

    def test_login_required_allows_authenticated(self, client, monkeypatch):
        """Test that login_required allows authenticated users."""
        # Mock the database manager to simulate having a connection
        from src.local_deep_research.database.encrypted_db import db_manager
        from unittest.mock import MagicMock

        # Mock the connections dictionary to have an entry for our test user
        mock_connections = {"testuser": MagicMock()}
        monkeypatch.setattr(db_manager, "connections", mock_connections)

        # Mock get_session to return a valid session
        mock_session = MagicMock()
        monkeypatch.setattr(
            db_manager,
            "get_session",
            lambda username: mock_session if username == "testuser" else None,
        )

        with client.session_transaction() as sess:
            sess["username"] = "testuser"

        response = client.get("/protected")
        assert response.status_code == 200
        assert b"Protected content" in response.data

    def test_public_route_accessible(self, client):
        """Test that public routes are accessible without auth."""
        response = client.get("/public")
        assert response.status_code == 200
        assert b"Public content" in response.data

    def test_current_user_function(self, client, monkeypatch):
        """Test the current_user helper function."""
        # Mock the database manager to simulate having a connection
        from src.local_deep_research.database.encrypted_db import db_manager
        from unittest.mock import MagicMock

        # Mock for logged in user
        mock_connections = {"testuser": MagicMock()}
        monkeypatch.setattr(db_manager, "connections", mock_connections)
        mock_session = MagicMock()
        monkeypatch.setattr(
            db_manager,
            "get_session",
            lambda username: mock_session if username == "testuser" else None,
        )

        # Test not logged in scenario
        with client.session_transaction() as sess:
            assert "username" not in sess

        # Make a request to establish request context
        response = client.get("/public")
        assert response.status_code == 200

        # Test current_user during a request
        with client.application.test_request_context("/"):
            # Set up an empty session to simulate not logged in
            with client.session_transaction() as sess:
                sess.clear()
            assert current_user() is None

        # Test logged in scenario
        with client.session_transaction() as sess:
            sess["username"] = "testuser"

        response = client.get("/user-info")
        assert response.status_code == 200
        assert b"User: testuser" in response.data

    def test_inject_current_user(self, app, client, monkeypatch):
        """Test that current user is injected into g."""
        # Mock the database manager
        from src.local_deep_research.database.encrypted_db import db_manager
        from unittest.mock import MagicMock

        # Mock for logged in user
        mock_connections = {"testuser": MagicMock()}
        monkeypatch.setattr(db_manager, "connections", mock_connections)
        mock_session = MagicMock()
        monkeypatch.setattr(
            db_manager,
            "get_session",
            lambda username: mock_session if username == "testuser" else None,
        )

        # Test not logged in
        with app.test_request_context("/"):
            # Ensure session is empty
            if "username" in session:
                session.pop("username")
            inject_current_user()
            assert g.current_user is None
            assert g.db_session is None

        # Test logged in
        with app.test_request_context("/"):
            # Set session data directly within request context
            session["username"] = "testuser"
            inject_current_user()
            assert g.current_user == "testuser"
            assert g.db_session == mock_session

    def test_login_required_with_missing_db_connection(
        self, client, monkeypatch
    ):
        """Test login_required when database connection is missing."""

        # Mock db_manager to simulate missing connection
        class MockDbManager:
            connections = {}

        import src.local_deep_research.web.auth.decorators as decorators

        monkeypatch.setattr(decorators, "db_manager", MockDbManager())

        with client.session_transaction() as sess:
            sess["username"] = "testuser"

        response = client.get("/protected")
        assert response.status_code == 302
        assert "/auth/login" in response.location

        # Session should be cleared
        with client.session_transaction() as sess:
            assert "username" not in sess
