# [DEF:Dependencies:Module]
# @SEMANTICS: dependency, injection, singleton, factory, auth, jwt
# @PURPOSE: Manages the creation and provision of shared application dependencies, such as the PluginLoader and TaskManager, to avoid circular imports.
# @LAYER: Core
# @RELATION: Used by the main app and API routers to get access to shared instances.

from pathlib import Path
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError
from .core.plugin_loader import PluginLoader
from .core.task_manager import TaskManager
from .core.config_manager import ConfigManager
from .core.scheduler import SchedulerService
from .core.database import init_db, get_auth_db
from .core.logger import logger, belief_scope
from .core.auth.jwt import decode_token
from .core.auth.repository import AuthRepository
from .models.auth import User

# Initialize singletons
# Use absolute path relative to this file to ensure plugins are found regardless of CWD
project_root = Path(__file__).parent.parent.parent
config_path = project_root / "config.json"
config_manager = ConfigManager(config_path=str(config_path))

# Initialize database before any other services that might use it
init_db()

# [DEF:get_config_manager:Function]
# @PURPOSE: Dependency injector for the ConfigManager.
# @PRE: Global config_manager must be initialized.
# @POST: Returns shared ConfigManager instance.
# @RETURN: ConfigManager - The shared config manager instance.
def get_config_manager() -> ConfigManager:
    """Dependency injector for the ConfigManager."""
    return config_manager
# [/DEF:get_config_manager:Function]

plugin_dir = Path(__file__).parent / "plugins"

plugin_loader = PluginLoader(plugin_dir=str(plugin_dir))
logger.info(f"PluginLoader initialized with directory: {plugin_dir}")
logger.info(f"Available plugins: {[config.name for config in plugin_loader.get_all_plugin_configs()]}")

task_manager = TaskManager(plugin_loader)
logger.info("TaskManager initialized")

scheduler_service = SchedulerService(task_manager, config_manager)
logger.info("SchedulerService initialized")

# [DEF:get_plugin_loader:Function]
# @PURPOSE: Dependency injector for the PluginLoader.
# @PRE: Global plugin_loader must be initialized.
# @POST: Returns shared PluginLoader instance.
# @RETURN: PluginLoader - The shared plugin loader instance.
def get_plugin_loader() -> PluginLoader:
    """Dependency injector for the PluginLoader."""
    return plugin_loader
# [/DEF:get_plugin_loader:Function]

# [DEF:get_task_manager:Function]
# @PURPOSE: Dependency injector for the TaskManager.
# @PRE: Global task_manager must be initialized.
# @POST: Returns shared TaskManager instance.
# @RETURN: TaskManager - The shared task manager instance.
def get_task_manager() -> TaskManager:
    """Dependency injector for the TaskManager."""
    return task_manager
# [/DEF:get_task_manager:Function]

# [DEF:get_scheduler_service:Function]
# @PURPOSE: Dependency injector for the SchedulerService.
# @PRE: Global scheduler_service must be initialized.
# @POST: Returns shared SchedulerService instance.
# @RETURN: SchedulerService - The shared scheduler service instance.
def get_scheduler_service() -> SchedulerService:
    """Dependency injector for the SchedulerService."""
    return scheduler_service
# [/DEF:get_scheduler_service:Function]

# [DEF:oauth2_scheme:Variable]
# @PURPOSE: OAuth2 password bearer scheme for token extraction.
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")
# [/DEF:oauth2_scheme:Variable]

# [DEF:get_current_user:Function]
# @PURPOSE: Dependency for retrieving the currently authenticated user from a JWT.
# @PRE:     JWT token provided in Authorization header.
# @POST:    Returns the User object if token is valid.
# @THROW:   HTTPException 401 if token is invalid or user not found.
# @PARAM:   token (str) - Extracted JWT token.
# @PARAM:   db (Session) - Auth database session.
# @RETURN:  User - The authenticated user.
def get_current_user(token: str = Depends(oauth2_scheme), db = Depends(get_auth_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = decode_token(token)
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
        
    repo = AuthRepository(db)
    user = repo.get_user_by_username(username)
    if user is None:
        raise credentials_exception
    return user
# [/DEF:get_current_user:Function]

# [DEF:has_permission:Function]
# @PURPOSE: Dependency for checking if the current user has a specific permission.
# @PRE:     User is authenticated.
# @POST:    Returns True if user has permission.
# @THROW:   HTTPException 403 if permission is denied.
# @PARAM:   resource (str) - The resource identifier.
# @PARAM:   action (str) - The action identifier (READ, EXECUTE, WRITE).
# @RETURN:  User - The authenticated user if permission granted.
def has_permission(resource: str, action: str):
    def permission_checker(current_user: User = Depends(get_current_user)):
        # Union of all permissions across all roles
        for role in current_user.roles:
            for perm in role.permissions:
                if perm.resource == resource and perm.action == action:
                    return current_user
        
        # Special case for Admin role (full access)
        if any(role.name == "Admin" for role in current_user.roles):
            return current_user
        
        from .core.auth.logger import log_security_event
        log_security_event("PERMISSION_DENIED", current_user.username, {"resource": resource, "action": action})
            
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"Permission denied for {resource}:{action}"
        )
    return permission_checker
# [/DEF:has_permission:Function]

# [/DEF:Dependencies:Module]