"""Unit tests for setup redirect middleware."""
import pytest
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient

from src.server.middleware.setup_redirect import SetupRedirectMiddleware
from src.server.services.auth_service import auth_service
from src.server.services.config_service import get_config_service


@pytest.fixture
def app():
    """Create a test FastAPI application."""
    app = FastAPI()
    
    # Add the middleware
    app.add_middleware(SetupRedirectMiddleware)
    
    # Add test routes
    @app.get("/")
    async def root():
        return {"message": "Home page"}
    
    @app.get("/setup")
    async def setup():
        return {"message": "Setup page"}
    
    @app.get("/login")
    async def login():
        return {"message": "Login page"}
    
    @app.get("/api/health")
    async def health():
        return {"status": "ok"}
    
    @app.get("/api/auth/status")
    async def auth_status():
        return {"configured": auth_service.is_configured()}
    
    @app.get("/api/data")
    async def api_data():
        return {"data": "some data"}
    
    return app


@pytest.fixture
async def client(app):
    """Create an async test client."""
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as ac:
        yield ac


@pytest.fixture(autouse=True)
def reset_auth_service():
    """Reset auth service state before each test."""
    # Save original state
    original_hash = auth_service._hash
    
    # Reset for test
    auth_service._hash = None
    
    yield
    
    # Restore original state
    auth_service._hash = original_hash


@pytest.fixture(autouse=True)
def reset_config_service():
    """Reset config service state before each test."""
    config_service = get_config_service()
    
    # Backup original config path
    original_path = config_service.config_path
    
    # Create a temporary config path
    import tempfile
    from pathlib import Path
    
    temp_dir = Path(tempfile.mkdtemp())
    config_service.config_path = temp_dir / "config.json"
    config_service.backup_dir = temp_dir / "backups"
    
    yield
    
    # Restore original path
    config_service.config_path = original_path
    
    # Clean up temp directory
    import shutil
    shutil.rmtree(temp_dir, ignore_errors=True)


class TestSetupRedirectMiddleware:
    """Test cases for setup redirect middleware."""
    
    @pytest.mark.asyncio
    async def test_redirect_to_setup_when_not_configured(self, client):
        """Test that HTML requests redirect to /setup when not configured."""
        # Request home page with HTML accept header (don't follow redirects)
        response = await client.get(
            "/", headers={"Accept": "text/html"}, follow_redirects=False
        )
        
        # Should redirect to setup
        assert response.status_code == 302
        assert response.headers["location"] == "/setup"
    
    @pytest.mark.asyncio
    async def test_setup_page_accessible_without_config(self, client):
        """Test that /setup page is accessible when not configured."""
        response = await client.get("/setup")
        
        # Should not redirect
        assert response.status_code == 200
        assert response.json()["message"] == "Setup page"
    
    @pytest.mark.asyncio
    async def test_api_returns_503_when_not_configured(self, client):
        """Test that API requests return 503 when not configured."""
        response = await client.get("/api/data")
        
        # Should return 503 Service Unavailable
        assert response.status_code == 503
        assert "setup_url" in response.json()
        assert response.json()["setup_url"] == "/setup"
    
    @pytest.mark.asyncio
    async def test_exempt_api_endpoints_accessible(self, client):
        """Test that exempt API endpoints are accessible without setup."""
        # Health endpoint should be accessible
        response = await client.get("/api/health")
        assert response.status_code == 200
        assert response.json()["status"] == "ok"
        
        # Auth status endpoint should be accessible
        response = await client.get("/api/auth/status")
        assert response.status_code == 200
        assert response.json()["configured"] is False
    
    @pytest.mark.asyncio
    async def test_no_redirect_when_configured(self, client):
        """Test that no redirect happens when auth and config are set up."""
        # Configure auth service
        auth_service.setup_master_password("Test@Password123")
        
        # Create a valid config
        config_service = get_config_service()
        from src.server.models.config import AppConfig
        config = AppConfig()
        config_service.save_config(config, create_backup=False)
        
        # Request home page
        response = await client.get("/", headers={"Accept": "text/html"})
        
        # Should not redirect
        assert response.status_code == 200
        assert response.json()["message"] == "Home page"
    
    @pytest.mark.asyncio
    async def test_api_works_when_configured(self, client):
        """Test that API requests work normally when configured."""
        # Configure auth service
        auth_service.setup_master_password("Test@Password123")
        
        # Create a valid config
        config_service = get_config_service()
        from src.server.models.config import AppConfig
        config = AppConfig()
        config_service.save_config(config, create_backup=False)
        
        # Request API endpoint
        response = await client.get("/api/data")
        
        # Should work normally
        assert response.status_code == 200
        assert response.json()["data"] == "some data"
    
    @pytest.mark.asyncio
    async def test_static_files_always_accessible(self, client, app):
        """Test that static file paths are always accessible."""
        # Create a route that mimics static file serving
        @app.get("/static/css/style.css")
        async def static_css():
            return {"content": "css"}
        
        # Request static file
        response = await client.get("/static/css/style.css")
        
        # Should be accessible even without setup
        assert response.status_code == 200
    
    @pytest.mark.asyncio
    async def test_redirect_when_only_auth_configured(self, client):
        """Test redirect when auth is configured but config is invalid."""
        # Configure auth but don't create config file
        auth_service.setup_master_password("Test@Password123")
        
        # Request home page
        response = await client.get("/", headers={"Accept": "text/html"})
        
        # Should still work because load_config creates default config
        # This is the current behavior - may need to adjust if we want
        # stricter setup requirements
        assert response.status_code in [200, 302]
    
    @pytest.mark.asyncio
    async def test_root_path_redirect(self, client):
        """Test that root path redirects to setup when not configured."""
        response = await client.get(
            "/", headers={"Accept": "text/html"}, follow_redirects=False
        )
        
        # Should redirect to setup
        assert response.status_code == 302
        assert response.headers["location"] == "/setup"
    
    def test_path_matching_exact_and_prefix(self):
        """Test that path matching works for both exact and prefix."""
        middleware = SetupRedirectMiddleware(app=FastAPI())
        
        # Exact matches
        assert middleware._is_path_exempt("/setup") is True
        assert middleware._is_path_exempt("/api/health") is True
        
        # Prefix matches
        assert middleware._is_path_exempt("/static/css/style.css") is True
        assert middleware._is_path_exempt("/static/js/app.js") is True
        
        # Non-exempt paths
        assert middleware._is_path_exempt("/dashboard") is False
        assert middleware._is_path_exempt("/api/data") is False


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
