"""Setup redirect middleware for Aniworld.

This middleware ensures that users are redirected to the setup page
if the application is not properly configured. It checks if both the
master password and basic configuration exist before allowing access
to other parts of the application.
"""
from __future__ import annotations

from typing import Callable

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import RedirectResponse, Response
from starlette.types import ASGIApp

from src.server.services.auth_service import auth_service
from src.server.services.config_service import get_config_service


class SetupRedirectMiddleware(BaseHTTPMiddleware):
    """Middleware that redirects to /setup if configuration is incomplete.
    
    The middleware checks:
    1. If master password is configured (via auth_service.is_configured())
    2. If configuration file exists and is valid
    
    If either check fails, users are redirected to /setup page,
    except for whitelisted paths that must remain accessible.
    """
    
    # Paths that should always be accessible, even without setup
    EXEMPT_PATHS = {
        "/setup",  # Setup page itself
        "/loading",  # Loading page (initialization progress)
        "/login",  # Login page (needs to be accessible after setup)
        "/queue",  # Queue page (for initial load)
        "/api/auth/",  # All auth endpoints (setup, login, logout, register)
        "/ws/connect",  # WebSocket connection (needed for loading page)
        "/api/queue/",  # Queue API endpoints
        "/api/downloads/",  # Download API endpoints
        "/api/config/",  # Config API (needed for setup and management)
        "/api/anime/",  # Anime API endpoints
        "/api/health",  # Health check
        "/health",  # Health check (alternate path)
        "/api/docs",  # API documentation
        "/api/redoc",  # ReDoc documentation
        "/openapi.json",  # OpenAPI schema
        "/static/",  # Static files (CSS, JS, images)
    }
    
    def __init__(self, app: ASGIApp) -> None:
        """Initialize the setup redirect middleware.
        
        Args:
            app: The ASGI application
        """
        super().__init__(app)
    
    def _is_path_exempt(self, path: str) -> bool:
        """Check if a path is exempt from setup redirect.
        
        Args:
            path: The request path to check
            
        Returns:
            True if the path should be accessible without setup
        """
        # Exact matches
        if path in self.EXEMPT_PATHS:
            return True
        
        # Prefix matches (e.g., /static/, /api/config)
        for exempt_path in self.EXEMPT_PATHS:
            if exempt_path.endswith("/") and path.startswith(exempt_path):
                return True
        
        return False
    
    def _needs_setup(self) -> bool:
        """Check if the application needs initial setup.
        
        Returns:
            True if setup is required, False otherwise
        """
        # Check if master password is configured
        if not auth_service.is_configured():
            return True
        
        # Check if config exists and is valid
        try:
            config_service = get_config_service()
            config = config_service.load_config()
            
            # Validate the loaded config
            validation = config.validate_config()
            if not validation.valid:
                return True
                
        except (FileNotFoundError, ValueError, OSError, AttributeError):
            # If we can't load or validate config, setup is needed
            return True
        
        return False
    
    async def dispatch(
        self, request: Request, call_next: Callable
    ) -> Response:
        """Process the request and redirect to setup if needed.
        
        Args:
            request: The incoming request
            call_next: The next middleware or route handler
            
        Returns:
            Either a redirect to /setup or the normal response
        """
        path = request.url.path
        
        # Check if trying to access setup or loading page after completion
        if path in ("/setup", "/loading"):
            if not self._needs_setup():
                # Setup is complete, check loading status
                if path == "/setup":
                    # Redirect to loading if initialization is in progress
                    # Otherwise redirect to login
                    return RedirectResponse(url="/login", status_code=302)
                elif path == "/loading":
                    # Check if initialization is complete
                    try:
                        from src.server.database.connection import get_db_session
                        from src.server.database.system_settings_service import (
                            SystemSettingsService,
                        )
                        
                        async with get_db_session() as db:
                            is_complete = await SystemSettingsService.is_initial_scan_completed(db)
                            if is_complete:
                                # Initialization complete, redirect to login
                                return RedirectResponse(url="/login", status_code=302)
                    except Exception:
                        # If we can't check, allow access to loading page
                        pass
        
        # Skip setup check for exempt paths
        if self._is_path_exempt(path):
            return await call_next(request)
        
        # Check if setup is needed
        if self._needs_setup():
            # Redirect to setup page for HTML requests
            # Return 503 for API requests
            accept_header = request.headers.get("accept", "")
            if "text/html" in accept_header or path == "/":
                return RedirectResponse(url="/setup", status_code=302)
            else:
                # For API requests, return JSON error
                from fastapi.responses import JSONResponse
                return JSONResponse(
                    status_code=503,
                    content={
                        "detail": "Application setup required",
                        "setup_url": "/setup"
                    }
                )
        
        # Setup is complete, continue normally
        return await call_next(request)
