"""Authentication API endpoints for Aniworld."""
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException
from fastapi import status as http_status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

from src.server.models.auth import (
    AuthStatus,
    LoginRequest,
    LoginResponse,
    RegisterRequest,
    SetupRequest,
)
from src.server.models.config import AppConfig
from src.server.services.auth_service import AuthError, LockedOutError, auth_service
from src.server.services.config_service import get_config_service

# NOTE: import dependencies (optional_auth, security) lazily inside handlers
# to avoid importing heavyweight modules (e.g. sqlalchemy) at import time.

router = APIRouter(prefix="/api/auth", tags=["auth"])

# HTTPBearer for optional authentication
optional_bearer = HTTPBearer(auto_error=False)


@router.post("/setup", status_code=http_status.HTTP_201_CREATED)
async def setup_auth(req: SetupRequest):
    """Initial setup endpoint to configure the master password.
    
    This endpoint also initializes the configuration with all provided values
    and saves them to config.json. It triggers background initialization
    and redirects to a loading page that shows real-time progress.
    """
    if auth_service.is_configured():
        raise HTTPException(
            status_code=http_status.HTTP_400_BAD_REQUEST,
            detail="Master password already configured",
        )

    try:
        # Set up master password (this validates and hashes it)
        password_hash = auth_service.setup_master_password(
            req.master_password
        )
        
        # Initialize or update config with all provided values
        config_service = get_config_service()
        try:
            config = config_service.load_config()
        except Exception:
            # If config doesn't exist, create default
            from src.server.models.config import (
                BackupConfig,
                LoggingConfig,
                NFOConfig,
                SchedulerConfig,
            )
            config = AppConfig()
        
        # Update basic settings
        if req.name:
            config.name = req.name
        if req.data_dir:
            config.data_dir = req.data_dir
        
        # Update scheduler configuration
        if req.scheduler_enabled is not None:
            config.scheduler.enabled = req.scheduler_enabled
        if req.scheduler_interval_minutes is not None:
            config.scheduler.interval_minutes = req.scheduler_interval_minutes
        if req.scheduler_schedule_time is not None:
            config.scheduler.schedule_time = req.scheduler_schedule_time
        if req.scheduler_schedule_days is not None:
            config.scheduler.schedule_days = req.scheduler_schedule_days
        if req.scheduler_auto_download_after_rescan is not None:
            config.scheduler.auto_download_after_rescan = req.scheduler_auto_download_after_rescan
        if req.scheduler_folder_scan_enabled is not None:
            config.scheduler.folder_scan_enabled = req.scheduler_folder_scan_enabled
        
        # Update logging configuration
        if req.logging_level:
            config.logging.level = req.logging_level.upper()
        if req.logging_file is not None:
            config.logging.file = req.logging_file
        if req.logging_max_bytes is not None:
            config.logging.max_bytes = req.logging_max_bytes
        if req.logging_backup_count is not None:
            config.logging.backup_count = req.logging_backup_count
        
        # Update backup configuration
        if req.backup_enabled is not None:
            config.backup.enabled = req.backup_enabled
        if req.backup_path:
            config.backup.path = req.backup_path
        if req.backup_keep_days is not None:
            config.backup.keep_days = req.backup_keep_days
        
        # Update NFO configuration
        if req.nfo_tmdb_api_key is not None:
            config.nfo.tmdb_api_key = req.nfo_tmdb_api_key
        if req.nfo_auto_create is not None:
            config.nfo.auto_create = req.nfo_auto_create
        if req.nfo_update_on_scan is not None:
            config.nfo.update_on_scan = req.nfo_update_on_scan
        if req.nfo_download_poster is not None:
            config.nfo.download_poster = req.nfo_download_poster
        if req.nfo_download_logo is not None:
            config.nfo.download_logo = req.nfo_download_logo
        if req.nfo_download_fanart is not None:
            config.nfo.download_fanart = req.nfo_download_fanart
        if req.nfo_image_size:
            config.nfo.image_size = req.nfo_image_size.lower()
        
        # Store master password hash in config's other field
        config.other['master_password_hash'] = password_hash
        
        # Store anime directory in config's other field if provided
        anime_directory = None
        if req.anime_directory:
            anime_directory = req.anime_directory.strip()
            if anime_directory:
                config.other['anime_directory'] = anime_directory
        
        # Save the config with all updates
        config_service.save_config(config, create_backup=False)
        
        # Sync config.json values to settings object
        # (mirroring the logic in fastapi_app.py lifespan)
        from src.config.settings import settings
        other_settings = dict(config.other) if config.other else {}
        if other_settings.get("anime_directory"):
            settings.anime_directory = str(other_settings["anime_directory"])
        
        if config.nfo:
            if config.nfo.tmdb_api_key:
                settings.tmdb_api_key = config.nfo.tmdb_api_key
            settings.nfo_auto_create = config.nfo.auto_create
            settings.nfo_update_on_scan = config.nfo.update_on_scan
            settings.nfo_download_poster = config.nfo.download_poster
            settings.nfo_download_logo = config.nfo.download_logo
            settings.nfo_download_fanart = config.nfo.download_fanart
            settings.nfo_image_size = config.nfo.image_size
        
        # Trigger initialization in background task
        import asyncio

        from src.server.services.initialization_service import (
            perform_initial_setup,
            perform_nfo_scan_if_needed,
        )
        from src.server.services.progress_service import get_progress_service
        
        progress_service = get_progress_service()
        
        async def run_initialization():
            """Run initialization steps with progress updates."""
            try:
                # Perform the initial series sync and mark as completed
                await perform_initial_setup(progress_service)
                
                # Perform NFO scan if configured
                await perform_nfo_scan_if_needed(progress_service)
                
                # Start scheduler if anime_directory is now set
                try:
                    from src.server.services.scheduler_service import (
                        get_scheduler_service,
                    )

                    scheduler_svc = get_scheduler_service()
                    logger.info("Starting scheduler after initialization")
                    await scheduler_svc.ensure_started()
                    logger.info("Scheduler started successfully during setup")
                except Exception as sched_exc:
                    logger.warning(
                        "Failed to start scheduler during setup: %s", sched_exc
                    )
                    # Continue — scheduler failure should not break initialization
                
                # Send completion event
                from src.server.services.progress_service import ProgressType
                await progress_service.start_progress(
                    progress_id="initialization_complete",
                    progress_type=ProgressType.SYSTEM,
                    title="Initialization Complete",
                    total=100,
                    message="All initialization tasks completed successfully",
                    metadata={"initialization_complete": True}
                )
                await progress_service.complete_progress(
                    progress_id="initialization_complete",
                    message="All initialization tasks completed successfully",
                    metadata={"initialization_complete": True}
                )
            except Exception as e:
                # Send error event
                from src.server.services.progress_service import ProgressType
                await progress_service.start_progress(
                    progress_id="initialization_error",
                    progress_type=ProgressType.ERROR,
                    title="Initialization Failed",
                    total=100,
                    message=str(e),
                    metadata={"initialization_complete": True, "error": str(e)}
                )
                await progress_service.fail_progress(
                    progress_id="initialization_error",
                    error_message=str(e),
                    metadata={"initialization_complete": True, "error": str(e)}
                )
        
        # Start initialization in background
        asyncio.create_task(run_initialization())
        
        # Return redirect to loading page
        return {"status": "ok", "redirect": "/loading"}
        # Note: Media scan is skipped during setup as it requires
        # background_loader service which is only available during
        # application lifespan. It will run on first application startup.
        
        return {"status": "ok"}
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e)) from e


@router.post("/login", response_model=LoginResponse)
def login(req: LoginRequest):
    """Validate master password and return JWT token."""
    # Use a simple identifier for failed attempts; prefer IP in real app
    identifier = "global"

    try:
        valid = auth_service.validate_master_password(
            req.password, identifier=identifier
        )
    except LockedOutError as e:
        raise HTTPException(
            status_code=http_status.HTTP_429_TOO_MANY_REQUESTS,
            detail=str(e),
        ) from e
    except AuthError as e:
        # Return 401 for authentication errors (including not configured)
        # This prevents information leakage about system configuration
        raise HTTPException(
            status_code=http_status.HTTP_401_UNAUTHORIZED,
            detail="Invalid credentials"
        ) from e

    if not valid:
        raise HTTPException(
            status_code=http_status.HTTP_401_UNAUTHORIZED,
            detail="Invalid credentials"
        )

    token = auth_service.create_access_token(
        subject="master", remember=bool(req.remember)
    )
    return token


@router.post("/logout")
def logout_endpoint(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(
        optional_bearer
    ),
):
    """Logout by revoking token (no-op for stateless JWT)."""
    # If a plain credentials object was provided, extract token
    token = getattr(credentials, "credentials", None)
    # Placeholder; auth_service.revoke_token can be expanded to persist
    # revocations
    if token:
        auth_service.revoke_token(token)
    return {"status": "ok", "message": "Logged out successfully"}


async def get_optional_auth(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(
        optional_bearer
    ),
) -> Optional[dict]:
    """Get optional authentication from bearer token."""
    if credentials is None:
        return None

    token = credentials.credentials
    try:
        # Validate and decode token using the auth service
        session = auth_service.create_session_model(token)
        return session.model_dump()
    except AuthError:
        return None


@router.get("/status", response_model=AuthStatus)
async def auth_status(auth: Optional[dict] = Depends(get_optional_auth)):
    """Return whether master password is configured and authenticated."""
    return AuthStatus(
        configured=auth_service.is_configured(), authenticated=bool(auth)
    )


@router.post("/register", status_code=http_status.HTTP_201_CREATED)
def register(req: RegisterRequest):
    """Register a new user (for testing/validation purposes).
    
    Note: This is primarily for input validation testing.
    The actual Aniworld app uses a single master password.
    """
    # This endpoint is primarily for input validation testing
    # In a real multi-user system, you'd create the user here
    return {
        "status": "ok",
        "message": "User registration successful",
        "username": req.username,
    }

