"""
Step-up authentication middleware for high-risk operations.
Separates accountant and admin security concerns.
"""
from fastapi import Depends, HTTPException, status, Request
from sqlalchemy.orm import Session
from typing import Optional
from ...shared.config.database import get_db
from ...security.middleware.auth import get_current_user
from ...auth.models.user import User
from ...payments.services.accountant_security_service import accountant_security_service
from ...auth.services.admin_security_service import admin_security_service
from ...shared.utils.role_helpers import is_accountant, is_admin
from ...shared.config.logging_config import get_logger

logger = get_logger(__name__)


def require_step_up_auth(
    action_description: str = "this high-risk action"
):
    """
    Dependency to require step-up authentication for high-risk operations.
    """
    async def step_up_checker(
        request: Request,
        current_user: User = Depends(get_current_user),
        db: Session = Depends(get_db)
    ) -> User:
        # Only enforce for accountant roles; admins are handled separately
        if not is_accountant(current_user, db):
            return current_user  # Regular users don't need step-up
        
        # Get session token from request
        session_token = None
        # Try to get from custom header
        session_token = request.headers.get('X-Session-Token')
        # Or from cookie
        if not session_token:
            session_token = request.cookies.get('session_token')
        
        # Check if step-up is required
        requires_step_up, reason = accountant_security_service.require_step_up(
            db=db,
            user_id=current_user.id,
            session_token=session_token,
            action_description=action_description
        )
        
        if requires_step_up:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail={
                    'error': 'step_up_required',
                    'message': reason or f'Step-up authentication required for {action_description}',
                    'action': action_description
                }
            )
        
        return current_user
    
    return step_up_checker


def require_admin_step_up_auth(
    action_description: str = "this high-risk admin action"
):
    """
    Dependency to require step-up authentication for admin-only high-risk operations.
    Uses admin-specific security service, separate from accountant security.
    """
    async def step_up_checker(
        request: Request,
        current_user: User = Depends(get_current_user),
        db: Session = Depends(get_db)
    ) -> User:
        if not is_admin(current_user, db):
            return current_user  # Only admins are subject to this dependency

        # Try to get session token from header or cookie
        session_token = request.headers.get('X-Session-Token') or request.cookies.get('admin_session_token')

        # Check if step-up is required using admin security service
        requires_step_up, reason = admin_security_service.require_step_up(
            db=db,
            user_id=current_user.id,
            session_token=session_token,
            action_description=action_description
        )

        if requires_step_up:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail={
                    'error': 'step_up_required',
                    'message': reason or f'Step-up authentication required for {action_description}',
                    'action': action_description
                }
            )

        return current_user

    return step_up_checker


def enforce_mfa_for_accountants():
    """
    Dependency to enforce MFA for accountant/admin roles.
    This blocks access if MFA is required but not enabled.
    """
    async def mfa_enforcer(
        current_user: User = Depends(get_current_user),
        db: Session = Depends(get_db)
    ) -> User:
        # Only enforce for accountant roles; admins are handled separately
        if not is_accountant(current_user, db):
            return current_user  # Regular users don't need MFA
        
        # Check if MFA is required and enforced
        is_enforced, reason = accountant_security_service.is_mfa_enforced(current_user, db)
        
        if not is_enforced and reason:
            # CRITICAL: Block access instead of just logging warning
            logger.warning(
                f'User {current_user.id} ({current_user.email}) attempted to access financial data without MFA. '
                f'Access denied. Reason: {reason}'
            )
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail={
                    'error': 'mfa_required',
                    'message': reason or 'Multi-factor authentication is required for this role. Please enable MFA to access financial data.',
                    'requires_mfa_setup': True
                }
            )
        
        return current_user
    
    return mfa_enforcer


def authorize_financial_access(*allowed_roles: str):
    """
    Combined dependency that enforces both role authorization AND MFA for financial endpoints.
    Use this for all financial routes that require accountant/admin access.
    """
    def financial_access_checker(
        current_user: User = Depends(get_current_user),
        db: Session = Depends(get_db)
    ) -> User:
        from ...shared.utils.role_helpers import get_user_role_name
        from ...auth.models.role import Role
        
        # First check role authorization
        if hasattr(current_user, 'role') and current_user.role is not None:
            user_role_name = current_user.role.name
        else:
            role = db.query(Role).filter(Role.id == current_user.role_id).first()
            if not role:
                raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='User role not found')
            user_role_name = role.name
        
        # Check if user has required role
        if user_role_name not in allowed_roles:
            # Normalise accountant_* → accountant for role checks
            if (
                user_role_name.startswith("accountant_")
                and "accountant" in allowed_roles
            ):
                pass  # allowed
            else:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail='You do not have permission to access this resource'
                )
        
        # Then enforce MFA only for accountant roles (admins handled elsewhere)
        if is_accountant(current_user, db):
            is_enforced, reason = accountant_security_service.is_mfa_enforced(current_user, db)
            
            if not is_enforced and reason:
                # CRITICAL: Block access instead of just logging warning
                logger.warning(
                    f'User {current_user.id} ({current_user.email}) attempted to access financial data without MFA. '
                    f'Access denied. Reason: {reason}'
                )
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail={
                        'error': 'mfa_required',
                        'message': reason or 'Multi-factor authentication is required for this role. Please enable MFA to access financial data.',
                        'requires_mfa_setup': True
                    }
                )
        
        return current_user
    
    return financial_access_checker

