"""
Integration tests for the complete authentication system.
"""

import os
import shutil
import tempfile
from pathlib import Path

import pytest

from local_deep_research.database.auth_db import (
    get_auth_db_session,
    init_auth_database,
)
from local_deep_research.database.models.auth import User
from local_deep_research.web.app_factory import create_app


@pytest.fixture
def temp_data_dir():
    """Create a temporary data directory for testing."""
    temp_dir = tempfile.mkdtemp()
    yield Path(temp_dir)
    shutil.rmtree(temp_dir)


@pytest.fixture
def app(temp_data_dir, monkeypatch):
    """Create a Flask app configured for testing."""
    monkeypatch.setenv("LDR_DATA_DIR", str(temp_data_dir))

    # Clear database manager state before creating app
    from local_deep_research.database.encrypted_db import db_manager

    db_manager.close_all_databases()

    # Reset db_manager's data directory to temp directory
    db_manager.data_dir = temp_data_dir / "encrypted_databases"
    db_manager.data_dir.mkdir(parents=True, exist_ok=True)

    # Remove any existing user databases in the temp directory
    encrypted_db_dir = temp_data_dir / "encrypted_databases"
    if encrypted_db_dir.exists():
        shutil.rmtree(encrypted_db_dir)
        encrypted_db_dir.mkdir(parents=True, exist_ok=True)

    # Initialize fresh auth database
    init_auth_database()

    # Clean up any existing test users before starting
    auth_db = get_auth_db_session()
    auth_db.query(User).filter(User.username.like("integrationtest%")).delete()
    auth_db.query(User).filter(User.username.like("testuser%")).delete()
    auth_db.commit()
    auth_db.close()

    app, _ = create_app()
    app.config["TESTING"] = True
    app.config["WTF_CSRF_ENABLED"] = False
    app.config["SESSION_COOKIE_SECURE"] = False

    yield app

    # Cleanup after test
    db_manager.close_all_databases()

    # Clean up test users from auth database
    auth_db = get_auth_db_session()
    auth_db.query(User).filter(User.username.like("integrationtest%")).delete()
    auth_db.query(User).filter(User.username.like("testuser%")).delete()
    auth_db.commit()
    auth_db.close()


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


class TestAuthIntegration:
    """Integration tests for authentication system."""

    def test_full_user_lifecycle(self, client):
        """Test complete user lifecycle: register, login, use app, logout."""
        # 1. Start unauthenticated - should redirect to login
        response = client.get("/")
        assert response.status_code == 302
        assert "/auth/login" in response.location

        # 2. Register new user
        response = client.post(
            "/auth/register",
            data={
                "username": "integrationtest",
                "password": "TestPass123",  # pragma: allowlist secret
                "confirm_password": "TestPass123",  # pragma: allowlist secret
                "acknowledge": "true",
            },
            follow_redirects=True,
        )
        assert response.status_code == 200

        # 3. Should be logged in automatically after registration
        response = client.get("/")
        assert response.status_code == 200
        # The main page should show we're logged in (username might be displayed)

        # 4. Access protected routes
        response = client.get("/history")
        assert response.status_code == 200

        response = client.get("/settings")
        assert response.status_code == 200

        # 5. Check auth status
        response = client.get("/auth/check")
        assert response.status_code == 200
        data = response.get_json()
        assert data["authenticated"] is True
        assert data["username"] == "integrationtest"

        # 6. Logout
        response = client.post("/auth/logout", follow_redirects=True)
        assert response.status_code == 200
        assert b"You have been logged out successfully" in response.data

        # 7. Should not be able to access protected routes
        response = client.get("/history")
        assert response.status_code == 302
        assert "/auth/login" in response.location

        # 8. Login again
        response = client.post(
            "/auth/login",
            data={"username": "integrationtest", "password": "TestPass123"},
            follow_redirects=True,
        )
        assert response.status_code == 200

        # 9. Should be able to access protected routes again
        response = client.get("/history")
        assert response.status_code == 200

    def test_multiple_users(self, client):
        """Test that multiple users can register and have separate sessions."""
        # Register first user
        client.post(
            "/auth/register",
            data={
                "username": "user1",
                "password": "Password1X",
                "confirm_password": "Password1X",
                "acknowledge": "true",
            },
        )

        # Check logged in as user1
        response = client.get("/auth/check")
        data = response.get_json()
        assert data["username"] == "user1"

        # Logout
        client.post("/auth/logout")

        # Register second user
        client.post(
            "/auth/register",
            data={
                "username": "user2",
                "password": "Password2X",
                "confirm_password": "Password2X",
                "acknowledge": "true",
            },
        )

        # Check logged in as user2
        response = client.get("/auth/check")
        data = response.get_json()
        assert data["username"] == "user2"

        # Verify both users exist in auth database
        auth_db = get_auth_db_session()
        users = auth_db.query(User).all()
        assert len(users) == 2
        usernames = [user.username for user in users]
        assert "user1" in usernames
        assert "user2" in usernames
        auth_db.close()

    def test_session_persistence(self, client):
        """Test that sessions persist across requests."""
        # Register and login
        client.post(
            "/auth/register",
            data={
                "username": "sessiontest",
                "password": "TestPass123",  # pragma: allowlist secret
                "confirm_password": "TestPass123",  # pragma: allowlist secret
                "acknowledge": "true",
            },
        )

        # Make multiple requests
        for _ in range(5):
            response = client.get("/history")
            assert response.status_code == 200

            response = client.get("/auth/check")
            data = response.get_json()
            assert data["username"] == "sessiontest"

    def test_protected_api_endpoints(self, client):
        """Test that API endpoints require authentication."""
        # Test various API endpoints without auth
        endpoints = [
            ("/api/start_research", "POST"),
            ("/api/history", "GET"),
            ("/settings/api", "GET"),
            ("/history/api", "GET"),
            ("/metrics/api/metrics", "GET"),
        ]

        for endpoint, method in endpoints:
            if method == "GET":
                response = client.get(endpoint)
            else:
                response = client.post(endpoint, json={})

            # All these endpoints contain the slash-bounded `api` segment
            # (either /api/ in the middle or trailing /api), so the auth
            # decorator returns JSON 401 for unauthenticated callers.
            assert response.status_code == 401
            assert response.get_json()["error"] == "Authentication required"

        # Register and try again
        client.post(
            "/auth/register",
            data={
                "username": "apitest",
                "password": "TestPass123",  # pragma: allowlist secret
                "confirm_password": "TestPass123",  # pragma: allowlist secret
                "acknowledge": "true",
            },
        )

        # Now endpoints should be accessible (may return errors but not redirects)
        for endpoint, method in endpoints:
            if method == "GET":
                response = client.get(endpoint)
            else:
                response = client.post(endpoint, json={})

            assert response.status_code != 302  # Not a redirect

    @pytest.mark.skipif(
        os.environ.get("CI") == "true"
        or os.environ.get("GITHUB_ACTIONS") == "true",
        reason="Password change with encrypted DB re-keying is complex to test in CI",
    )
    def test_password_change_flow(self, client):
        """Test the complete password change flow."""
        # Register user
        client.post(
            "/auth/register",
            data={
                "username": "changetest",
                "password": "OldPass123",
                "confirm_password": "OldPass123",
                "acknowledge": "true",
            },
        )

        # Change password
        response = client.post(
            "/auth/change-password",
            data={
                "current_password": "OldPass123",
                "new_password": "NewPass456",  # pragma: allowlist secret
                "confirm_password": "NewPass456",
            },
            follow_redirects=True,
        )

        assert b"Password changed successfully" in response.data
        assert b"Please login with your new password" in response.data

        # Should be logged out
        response = client.get("/auth/check")
        assert response.status_code == 401

        # Old password should fail
        response = client.post(
            "/auth/login",
            data={"username": "changetest", "password": "OldPass123"},
        )
        assert response.status_code == 401

        # New password should work
        response = client.post(
            "/auth/login",
            data={"username": "changetest", "password": "NewPass456"},
            follow_redirects=True,
        )
        assert response.status_code == 200

        # Verify logged in
        response = client.get("/auth/check")
        data = response.get_json()
        assert data["username"] == "changetest"
