"""
Approval workflow service for enterprise financial operations.
"""
from sqlalchemy.orm import Session
from typing import Optional, Dict, Any, List
from datetime import datetime
from ..models.approval_workflow import ApprovalRequest, ApprovalStatus, ApprovalType
from ...auth.models.user import User
from ...shared.config.logging_config import get_logger
from ...analytics.services.audit_service import audit_service

logger = get_logger(__name__)

class ApprovalService:
    """Service for managing approval workflows."""
    
    # Thresholds for automatic approval requirements
    REFUND_THRESHOLD = 1000.0  # Require approval for refunds > $1000
    INVOICE_UPDATE_THRESHOLD = 500.0  # Require approval for invoice updates > $500
    LARGE_TRANSACTION_THRESHOLD = 5000.0  # Require approval for transactions > $5000
    
    @staticmethod
    def requires_approval(
        approval_type: ApprovalType,
        amount: Optional[float] = None,
        user_role: Optional[str] = None
    ) -> bool:
        """Check if an action requires approval."""
        # Admin and accountant can auto-approve their own actions
        if user_role in ['admin', 'accountant']:
            return False
        
        # Staff always requires approval for financial operations
        if user_role == 'staff':
            if approval_type in [
                ApprovalType.invoice_update,
                ApprovalType.payment_refund,
                ApprovalType.invoice_mark_paid,
                ApprovalType.financial_adjustment
            ]:
                return True
        
        # Check amount thresholds
        if amount:
            if approval_type == ApprovalType.payment_refund and amount > ApprovalService.REFUND_THRESHOLD:
                return True
            if approval_type == ApprovalType.invoice_update and amount > ApprovalService.INVOICE_UPDATE_THRESHOLD:
                return True
            if approval_type == ApprovalType.large_transaction and amount > ApprovalService.LARGE_TRANSACTION_THRESHOLD:
                return True
        
        return False
    
    @staticmethod
    async def create_approval_request(
        db: Session,
        approval_type: ApprovalType,
        resource_type: str,
        resource_id: int,
        requested_by: int,
        request_data: Dict[str, Any],
        current_data: Optional[Dict[str, Any]] = None,
        priority: str = 'normal',
        notes: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> ApprovalRequest:
        """Create a new approval request."""
        approval_request = ApprovalRequest(
            approval_type=approval_type,
            status=ApprovalStatus.pending,
            requested_by=requested_by,
            resource_type=resource_type,
            resource_id=resource_id,
            request_data=request_data,
            current_data=current_data,
            priority=priority,
            notes=notes,
            extra_metadata=metadata
        )
        
        db.add(approval_request)
        db.commit()
        db.refresh(approval_request)
        
        # Log approval request creation
        await audit_service.log_action(
            db=db,
            action='approval_request_created',
            resource_type='approval_request',
            user_id=requested_by,
            resource_id=approval_request.id,
            details={
                'approval_type': approval_type.value,
                'resource_type': resource_type,
                'resource_id': resource_id,
                'priority': priority
            },
            status='success'
        )
        
        logger.info(f'Approval request created: {approval_request.id} for {approval_type.value}')
        return approval_request
    
    @staticmethod
    async def approve_request(
        db: Session,
        request_id: int,
        approved_by: int,
        notes: Optional[str] = None
    ) -> ApprovalRequest:
        """Approve an approval request."""
        approval_request = db.query(ApprovalRequest).filter(
            ApprovalRequest.id == request_id
        ).first()
        
        if not approval_request:
            raise ValueError('Approval request not found')
        
        if approval_request.status != ApprovalStatus.pending:
            raise ValueError(f'Approval request is already {approval_request.status.value}')
        
        approval_request.status = ApprovalStatus.approved
        approval_request.approved_by = approved_by
        approval_request.approved_at = datetime.utcnow()
        if notes:
            approval_request.notes = notes
        
        db.commit()
        db.refresh(approval_request)
        
        # Log approval
        await audit_service.log_action(
            db=db,
            action='approval_request_approved',
            resource_type='approval_request',
            user_id=approved_by,
            resource_id=request_id,
            details={
                'approval_type': approval_request.approval_type.value,
                'resource_type': approval_request.resource_type,
                'resource_id': approval_request.resource_id
            },
            status='success'
        )
        
        logger.info(f'Approval request {request_id} approved by user {approved_by}')
        return approval_request
    
    @staticmethod
    async def reject_request(
        db: Session,
        request_id: int,
        rejected_by: int,
        rejection_reason: str
    ) -> ApprovalRequest:
        """Reject an approval request."""
        approval_request = db.query(ApprovalRequest).filter(
            ApprovalRequest.id == request_id
        ).first()
        
        if not approval_request:
            raise ValueError('Approval request not found')
        
        if approval_request.status != ApprovalStatus.pending:
            raise ValueError(f'Approval request is already {approval_request.status.value}')
        
        approval_request.status = ApprovalStatus.rejected
        approval_request.approved_by = rejected_by
        approval_request.approved_at = datetime.utcnow()
        approval_request.rejection_reason = rejection_reason
        
        db.commit()
        db.refresh(approval_request)
        
        # Log rejection
        await audit_service.log_action(
            db=db,
            action='approval_request_rejected',
            resource_type='approval_request',
            user_id=rejected_by,
            resource_id=request_id,
            details={
                'approval_type': approval_request.approval_type.value,
                'rejection_reason': rejection_reason
            },
            status='success'
        )
        
        logger.info(f'Approval request {request_id} rejected by user {rejected_by}')
        return approval_request
    
    @staticmethod
    def get_pending_approvals(
        db: Session,
        approval_type: Optional[ApprovalType] = None,
        limit: int = 50,
        offset: int = 0
    ) -> List[ApprovalRequest]:
        """Get pending approval requests."""
        query = db.query(ApprovalRequest).filter(
            ApprovalRequest.status == ApprovalStatus.pending
        )
        
        if approval_type:
            query = query.filter(ApprovalRequest.approval_type == approval_type)
        
        return query.order_by(
            ApprovalRequest.priority.desc(),
            ApprovalRequest.requested_at.asc()
        ).offset(offset).limit(limit).all()
    
    @staticmethod
    def get_user_approvals(
        db: Session,
        user_id: int,
        status: Optional[ApprovalStatus] = None,
        limit: int = 50,
        offset: int = 0
    ) -> List[ApprovalRequest]:
        """Get approval requests for a user."""
        query = db.query(ApprovalRequest).filter(
            ApprovalRequest.requested_by == user_id
        )
        
        if status:
            query = query.filter(ApprovalRequest.status == status)
        
        return query.order_by(ApprovalRequest.created_at.desc()).offset(offset).limit(limit).all()

approval_service = ApprovalService()

