"""
Routes for managing financial approval requests.
"""
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy.orm import Session
from typing import Optional
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import authorize_roles, get_current_user
from ...auth.models.user import User
from ..models.financial_approval import FinancialApproval, ApprovalStatus, ApprovalActionType
from ..services.approval_service import approval_service
from ...shared.utils.response_helpers import success_response
from ..services.financial_audit_service import financial_audit_service
from ..models.financial_audit_trail import FinancialActionType

logger = get_logger(__name__)
router = APIRouter(prefix='/financial/approvals', tags=['financial-approvals'])


@router.get('')
async def get_approvals(
    status: Optional[str] = Query(None),
    action_type: Optional[str] = Query(None),
    requested_by: Optional[int] = Query(None),
    approved_by: Optional[int] = Query(None),
    page: int = Query(1, ge=1),
    limit: int = Query(50, ge=1, le=100),
    current_user: User = Depends(authorize_roles('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Get approval requests with optional filters."""
    try:
        approval_status = None
        if status:
            try:
                approval_status = ApprovalStatus(status)
            except ValueError:
                raise HTTPException(status_code=400, detail=f"Invalid status: {status}")
        
        approval_action_type = None
        if action_type:
            try:
                approval_action_type = ApprovalActionType(action_type)
            except ValueError:
                raise HTTPException(status_code=400, detail=f"Invalid action type: {action_type}")
        
        # Non-admins and non-approvers can only see their own requests
        from ...shared.utils.role_helpers import is_accountant_approver, is_admin, is_accountant
        if not (is_admin(current_user, db) or is_accountant_approver(current_user, db)):
            # Regular accountants can only see their own requests
            if is_accountant(current_user, db):
                requested_by = current_user.id
            else:
                raise HTTPException(status_code=403, detail='Forbidden: Insufficient permissions')
        
        approvals = approval_service.get_approvals(
            db=db,
            status=approval_status,
            action_type=approval_action_type,
            requested_by=requested_by,
            approved_by=approved_by,
            page=page,
            limit=limit
        )
        
        approval_list = []
        for approval in approvals:
            approval_list.append({
                'id': approval.id,
                'action_type': approval.action_type.value,
                'action_description': approval.action_description,
                'status': approval.status.value,
                'amount': float(approval.amount) if approval.amount else None,
                'payment_id': approval.payment_id,
                'invoice_id': approval.invoice_id,
                'booking_id': approval.booking_id,
                'requested_by': approval.requested_by,
                'requested_by_email': approval.requested_by_email,
                'approved_by': approval.approved_by,
                'approved_by_email': approval.approved_by_email,
                'request_reason': approval.request_reason,
                'response_notes': approval.approval_notes or approval.rejection_reason,
                'previous_value': approval.previous_value,
                'new_value': approval.new_value,
                'requested_at': approval.created_at.isoformat() if approval.created_at else None,
                'responded_at': (approval.approved_at or approval.rejected_at).isoformat() if (approval.approved_at or approval.rejected_at) else None,
                'metadata': approval.approval_metadata
            })
        
        return success_response(data=approval_list)
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error fetching approvals: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/pending')
async def get_pending_approvals(
    action_type: Optional[str] = Query(None),
    current_user: User = Depends(authorize_roles('admin', 'accountant_approver')),
    db: Session = Depends(get_db)
):
    """Get pending approval requests."""
    try:
        approval_action_type = None
        if action_type:
            try:
                approval_action_type = ApprovalActionType(action_type)
            except ValueError:
                raise HTTPException(status_code=400, detail=f"Invalid action type: {action_type}")
        
        approvals = approval_service.get_pending_approvals(db, approval_action_type)
        
        approval_list = []
        for approval in approvals:
            approval_list.append({
                'id': approval.id,
                'action_type': approval.action_type.value,
                'action_description': approval.action_description,
                'status': approval.status.value,
                'amount': float(approval.amount) if approval.amount else None,
                'payment_id': approval.payment_id,
                'invoice_id': approval.invoice_id,
                'booking_id': approval.booking_id,
                'requested_by': approval.requested_by,
                'requested_by_email': approval.requested_by_email,
                'request_reason': approval.request_reason,
                'previous_value': approval.previous_value,
                'new_value': approval.new_value,
                'created_at': approval.created_at.isoformat() if approval.created_at else None,
                'metadata': approval.approval_metadata
            })
        
        return success_response(data={'approvals': approval_list})
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error fetching pending approvals: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/{approval_id}')
async def get_approval(
    approval_id: int,
    current_user: User = Depends(authorize_roles('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Get a specific approval request."""
    try:
        approval = approval_service.get_approval_by_id(db, approval_id)
        if not approval:
            raise HTTPException(status_code=404, detail='Approval request not found')
        
        # Users can only view their own requests unless they're approvers/admins
        from ...shared.utils.role_helpers import is_accountant_approver, is_admin
        if not (is_admin(current_user, db) or is_accountant_approver(current_user, db)):
            if approval.requested_by != current_user.id:
                raise HTTPException(status_code=403, detail='Forbidden: You can only view your own approval requests')
        
        return success_response(data={
            'approval': {
                'id': approval.id,
                'action_type': approval.action_type.value,
                'action_description': approval.action_description,
                'status': approval.status.value,
                'amount': float(approval.amount) if approval.amount else None,
                'payment_id': approval.payment_id,
                'invoice_id': approval.invoice_id,
                'booking_id': approval.booking_id,
                'requested_by': approval.requested_by,
                'requested_by_email': approval.requested_by_email,
                'request_reason': approval.request_reason,
                'previous_value': approval.previous_value,
                'new_value': approval.new_value,
                'approved_by': approval.approved_by,
                'approved_by_email': approval.approved_by_email,
                'approval_notes': approval.approval_notes,
                'approved_at': approval.approved_at.isoformat() if approval.approved_at else None,
                'rejected_by': approval.rejected_by,
                'rejection_reason': approval.rejection_reason,
                'rejected_at': approval.rejected_at.isoformat() if approval.rejected_at else None,
                'created_at': approval.created_at.isoformat() if approval.created_at else None,
                'updated_at': approval.updated_at.isoformat() if approval.updated_at else None,
                'metadata': approval.approval_metadata
            }
        })
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error fetching approval: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{approval_id}/approve')
async def approve_request(
    request: Request,
    approval_id: int,
    approval_data: dict,
    current_user: User = Depends(authorize_roles('admin', 'accountant_approver')),
    db: Session = Depends(get_db)
):
    """Approve an approval request."""
    client_ip = request.client.host if request.client else None
    user_agent = request.headers.get('User-Agent')
    request_id = getattr(request.state, 'request_id', None)
    
    try:
        approval_notes = approval_data.get('notes', '')
        
        approval = approval_service.approve_request(
            db=db,
            approval_id=approval_id,
            approved_by=current_user.id,
            approval_notes=approval_notes
        )
        
        db.commit()
        
        # Log to financial audit trail
        try:
            financial_audit_service.log_financial_action(
                db=db,
                action_type=FinancialActionType.settings_changed,  # Using closest match
                performed_by=current_user.id,
                action_description=f'Approval request {approval_id} approved for {approval.action_type.value}',
                payment_id=approval.payment_id,
                invoice_id=approval.invoice_id,
                booking_id=approval.booking_id,
                amount=float(approval.amount) if approval.amount else None,
                metadata={
                    'approval_id': approval_id,
                    'action_type': approval.action_type.value,
                    'approval_notes': approval_notes,
                    'ip_address': client_ip,
                    'user_agent': user_agent,
                    'request_id': request_id
                },
                notes=f'Approval granted by {current_user.email}'
            )
        except Exception as e:
            logger.warning(f'Failed to log financial audit for approval: {e}')
        
        return success_response(
            data={'approval': {
                'id': approval.id,
                'status': approval.status.value,
                'approved_by': approval.approved_by,
                'approved_at': approval.approved_at.isoformat() if approval.approved_at else None
            }},
            message='Approval request approved successfully'
        )
    except ValueError as e:
        db.rollback()
        raise HTTPException(status_code=400, detail=str(e))
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error approving request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{approval_id}/reject')
async def reject_request(
    request: Request,
    approval_id: int,
    rejection_data: dict,
    current_user: User = Depends(authorize_roles('admin', 'accountant_approver')),
    db: Session = Depends(get_db)
):
    """Reject an approval request."""
    client_ip = request.client.host if request.client else None
    user_agent = request.headers.get('User-Agent')
    request_id = getattr(request.state, 'request_id', None)
    
    try:
        rejection_reason = rejection_data.get('reason', '')
        if not rejection_reason:
            raise HTTPException(status_code=400, detail='Rejection reason is required')
        
        approval = approval_service.reject_request(
            db=db,
            approval_id=approval_id,
            rejected_by=current_user.id,
            rejection_reason=rejection_reason
        )
        
        db.commit()
        
        # Log to financial audit trail
        try:
            financial_audit_service.log_financial_action(
                db=db,
                action_type=FinancialActionType.settings_changed,
                performed_by=current_user.id,
                action_description=f'Approval request {approval_id} rejected for {approval.action_type.value}',
                payment_id=approval.payment_id,
                invoice_id=approval.invoice_id,
                booking_id=approval.booking_id,
                amount=float(approval.amount) if approval.amount else None,
                metadata={
                    'approval_id': approval_id,
                    'action_type': approval.action_type.value,
                    'rejection_reason': rejection_reason,
                    'ip_address': client_ip,
                    'user_agent': user_agent,
                    'request_id': request_id
                },
                notes=f'Approval rejected by {current_user.email}: {rejection_reason}'
            )
        except Exception as e:
            logger.warning(f'Failed to log financial audit for rejection: {e}')
        
        return success_response(
            data={'approval': {
                'id': approval.id,
                'status': approval.status.value,
                'rejected_by': approval.rejected_by,
                'rejected_at': approval.rejected_at.isoformat() if approval.rejected_at else None
            }},
            message='Approval request rejected successfully'
        )
    except ValueError as e:
        db.rollback()
        raise HTTPException(status_code=400, detail=str(e))
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error rejecting request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/{approval_id}/respond')
async def respond_to_approval(
    request: Request,
    approval_id: int,
    response_data: dict,
    current_user: User = Depends(authorize_roles('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Respond to an approval request (approve or reject)."""
    client_ip = request.client.host if request.client else None
    user_agent = request.headers.get('User-Agent')
    request_id = getattr(request.state, 'request_id', None)
    
    try:
        status = response_data.get('status')
        response_notes = response_data.get('response_notes', '')
        
        if status not in ['approved', 'rejected']:
            raise HTTPException(status_code=400, detail='Status must be either "approved" or "rejected"')
        
        if status == 'approved':
            approval = approval_service.approve_request(
                db=db,
                approval_id=approval_id,
                approved_by=current_user.id,
                approval_notes=response_notes
            )
        else:
            if not response_notes:
                raise HTTPException(status_code=400, detail='Rejection reason is required')
            approval = approval_service.reject_request(
                db=db,
                approval_id=approval_id,
                rejected_by=current_user.id,
                rejection_reason=response_notes
            )
        
        db.commit()
        
        # Log to financial audit trail
        try:
            financial_audit_service.log_financial_action(
                db=db,
                action_type=FinancialActionType.settings_changed,
                performed_by=current_user.id,
                action_description=f'Approval request {approval_id} {status} for {approval.action_type.value}',
                payment_id=approval.payment_id,
                invoice_id=approval.invoice_id,
                booking_id=approval.booking_id,
                amount=float(approval.amount) if approval.amount else None,
                metadata={
                    'approval_id': approval_id,
                    'action_type': approval.action_type.value,
                    'status': status,
                    'response_notes': response_notes,
                    'ip_address': client_ip,
                    'user_agent': user_agent,
                    'request_id': request_id
                },
                notes=f'Approval {status} by {current_user.email}: {response_notes}'
            )
        except Exception as e:
            logger.warning(f'Failed to log financial audit for approval response: {e}')
        
        return success_response(
            data={
                'id': approval.id,
                'action_type': approval.action_type.value,
                'action_description': approval.action_description,
                'status': approval.status.value,
                'amount': float(approval.amount) if approval.amount else None,
                'payment_id': approval.payment_id,
                'invoice_id': approval.invoice_id,
                'booking_id': approval.booking_id,
                'requested_by': approval.requested_by,
                'requested_by_email': approval.requested_by_email,
                'approved_by': approval.approved_by,
                'approved_by_email': approval.approved_by_email,
                'request_reason': approval.request_reason,
                'response_notes': approval.approval_notes or approval.rejection_reason,
                'previous_value': approval.previous_value,
                'new_value': approval.new_value,
                'requested_at': approval.created_at.isoformat() if approval.created_at else None,
                'responded_at': (approval.approved_at or approval.rejected_at).isoformat() if (approval.approved_at or approval.rejected_at) else None,
                'metadata': approval.approval_metadata
            },
            message=f'Approval request {status} successfully'
        )
    except ValueError as e:
        db.rollback()
        raise HTTPException(status_code=400, detail=str(e))
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error responding to approval: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

