"""
Routes for financial audit trail access.
"""
from fastapi import APIRouter, Depends, HTTPException, Query, Response, Request, status
from sqlalchemy.orm import Session
from sqlalchemy.exc import ProgrammingError, OperationalError
from typing import Optional
from datetime import datetime, timedelta
import csv
import io
import json
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import authorize_roles
from ...security.middleware.step_up_auth import authorize_financial_access
from ...auth.models.user import User
from ..models.financial_audit_trail import FinancialAuditTrail, FinancialActionType
from ..services.financial_audit_service import financial_audit_service
from ...shared.utils.response_helpers import success_response

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


@router.get('/')
async def get_financial_audit_trail(
    request: Request,
    payment_id: Optional[int] = Query(None),
    invoice_id: Optional[int] = Query(None),
    booking_id: Optional[int] = Query(None),
    action_type: Optional[str] = Query(None),
    user_id: Optional[int] = Query(None),
    start_date: Optional[str] = Query(None),
    end_date: Optional[str] = Query(None),
    page: int = Query(1, ge=1),
    limit: int = Query(100, ge=1, le=1000),
    current_user: User = Depends(authorize_financial_access('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Get financial audit trail records with filters. Requires step-up authentication."""
    # SECURITY: Enforce MFA for sensitive operations like audit trail access
    try:
        from ...payments.services.accountant_security_service import accountant_security_service
        is_enforced, reason = accountant_security_service.is_mfa_enforced(current_user, db)
        if not is_enforced and reason:
            logger.warning(
                f'User {current_user.id} ({current_user.email}) attempted to access audit trail without MFA enabled. '
                f'Reason: {reason}'
            )
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f'Multi-factor authentication (MFA) is required for this operation. {reason}'
            )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error checking MFA enforcement for audit trail access: {str(e)}', exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail='Error verifying authentication requirements'
        )
    
    # Log activity
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        # Check for unusual activity
        is_unusual = accountant_security_service.detect_unusual_activity(
            db=db,
            user_id=current_user.id,
            ip_address=client_ip
        )
        
        accountant_security_service.log_activity(
            db=db,
            user_id=current_user.id,
            activity_type='audit_trail_access',
            activity_description='Financial audit trail accessed',
            ip_address=client_ip,
            user_agent=user_agent,
            risk_level='medium',
            is_unusual=is_unusual
        )
    except Exception as e:
        logger.warning(f'Error logging audit trail access: {e}')
    
    try:
        # Parse dates
        start = None
        end = None
        if start_date:
            start = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
        if end_date:
            end = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
        
        # Parse action type
        action_type_enum = None
        if action_type:
            try:
                action_type_enum = FinancialActionType(action_type)
            except ValueError:
                raise HTTPException(status_code=400, detail=f'Invalid action_type: {action_type}')
        
        # Calculate offset
        offset = (page - 1) * limit
        
        # Get audit trail
        records = financial_audit_service.get_audit_trail(
            db=db,
            payment_id=payment_id,
            invoice_id=invoice_id,
            booking_id=booking_id,
            action_type=action_type_enum,
            user_id=user_id,
            start_date=start,
            end_date=end,
            limit=limit,
            offset=offset
        )
        
        # Format response
        audit_data = []
        for record in records:
            audit_data.append({
                'id': record.id,
                'action_type': record.action_type.value,
                'action_description': record.action_description,
                'payment_id': record.payment_id,
                'invoice_id': record.invoice_id,
                'booking_id': record.booking_id,
                'amount': float(record.amount) if record.amount else None,
                'previous_amount': float(record.previous_amount) if record.previous_amount else None,
                'currency': record.currency,
                'performed_by': record.performed_by,
                'performed_by_email': record.performed_by_email,
                'metadata': record.audit_metadata,
                'notes': record.notes,
                'created_at': record.created_at.isoformat() if record.created_at else None
            })
        
        # Get total count for pagination
        try:
            total_query = db.query(FinancialAuditTrail)
            if payment_id:
                total_query = total_query.filter(FinancialAuditTrail.payment_id == payment_id)
            if invoice_id:
                total_query = total_query.filter(FinancialAuditTrail.invoice_id == invoice_id)
            if booking_id:
                total_query = total_query.filter(FinancialAuditTrail.booking_id == booking_id)
            if action_type_enum:
                total_query = total_query.filter(FinancialAuditTrail.action_type == action_type_enum)
            if user_id:
                total_query = total_query.filter(FinancialAuditTrail.performed_by == user_id)
            if start:
                total_query = total_query.filter(FinancialAuditTrail.created_at >= start)
            if end:
                total_query = total_query.filter(FinancialAuditTrail.created_at <= end)
            
            total_count = total_query.count()
            total_pages = (total_count + limit - 1) // limit
        except (ProgrammingError, OperationalError):
            # If table doesn't exist, count is 0
            total_count = 0
            total_pages = 0
        
        return success_response(
            data={
                'audit_trail': audit_data,
                'pagination': {
                    'page': page,
                    'limit': limit,
                    'total': total_count,
                    'total_pages': total_pages
                }
            },
            message='Financial audit trail retrieved successfully'
        )
    except HTTPException:
        raise
    except (ProgrammingError, OperationalError) as e:
        # Handle case where financial_audit_trail table doesn't exist
        error_msg = str(e)
        if 'doesn\'t exist' in error_msg or 'Table' in error_msg:
            logger.warning(f'Financial audit trail table not found: {error_msg}')
            return success_response(
                data={
                    'audit_trail': [],
                    'pagination': {
                        'page': page,
                        'limit': limit,
                        'total': 0,
                        'total_pages': 0
                    },
                    'note': 'Financial audit trail table not yet created. Please run database migrations.'
                },
                message='Financial audit trail is not available (table not created)'
            )
        logger.error(f'Database error retrieving financial audit trail: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Database error occurred while retrieving audit trail')
    except Exception as e:
        logger.error(f'Error retrieving financial audit trail: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while retrieving audit trail')


@router.get('/{record_id}')
async def get_audit_record(
    record_id: int,
    current_user: User = Depends(authorize_financial_access('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Get a specific audit trail record."""
    try:
        record = db.query(FinancialAuditTrail).filter(FinancialAuditTrail.id == record_id).first()
        if not record:
            raise HTTPException(status_code=404, detail='Audit record not found')
        
        return success_response(
            data={
                'id': record.id,
                'action_type': record.action_type.value,
                'action_description': record.action_description,
                'payment_id': record.payment_id,
                'invoice_id': record.invoice_id,
                'booking_id': record.booking_id,
                'amount': float(record.amount) if record.amount else None,
                'previous_amount': float(record.previous_amount) if record.previous_amount else None,
                'currency': record.currency,
                'performed_by': record.performed_by,
                'performed_by_email': record.performed_by_email,
                'metadata': record.audit_metadata,
                'notes': record.notes,
                'created_at': record.created_at.isoformat() if record.created_at else None
            },
            message='Audit record retrieved successfully'
        )
    except HTTPException:
        raise
    except (ProgrammingError, OperationalError) as e:
        # Handle case where financial_audit_trail table doesn't exist
        error_msg = str(e)
        if 'doesn\'t exist' in error_msg or 'Table' in error_msg:
            logger.warning(f'Financial audit trail table not found: {error_msg}')
            raise HTTPException(status_code=404, detail='Financial audit trail table not yet created. Please run database migrations.')
        logger.error(f'Database error retrieving audit record: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Database error occurred while retrieving audit record')
    except Exception as e:
        logger.error(f'Error retrieving audit record: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='An error occurred while retrieving audit record')


@router.get('/export')
async def export_audit_trail(
    request: Request,
    format: str = Query('csv', regex='^(csv|json)$'),
    payment_id: Optional[int] = Query(None),
    invoice_id: Optional[int] = Query(None),
    booking_id: Optional[int] = Query(None),
    action_type: Optional[str] = Query(None),
    user_id: Optional[int] = Query(None),
    start_date: Optional[str] = Query(None),
    end_date: Optional[str] = Query(None),
    current_user: User = Depends(authorize_financial_access('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Export financial audit trail to CSV or JSON. Requires step-up authentication."""
    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)
    
    # SECURITY: Enforce MFA for sensitive operations like audit trail export
    try:
        from ...payments.services.accountant_security_service import accountant_security_service
        is_enforced, reason = accountant_security_service.is_mfa_enforced(current_user, db)
        if not is_enforced and reason:
            logger.warning(
                f'User {current_user.id} ({current_user.email}) attempted to export audit trail without MFA enabled. '
                f'Reason: {reason}'
            )
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f'Multi-factor authentication (MFA) is required for this operation. {reason}'
            )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error checking MFA enforcement for audit trail export: {str(e)}', exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail='Error verifying authentication requirements'
        )
    
    try:
        # Parse dates
        start = None
        end = None
        if start_date:
            start = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
        if end_date:
            end = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
        
        # Parse action type
        action_type_enum = None
        if action_type:
            try:
                action_type_enum = FinancialActionType(action_type)
            except ValueError:
                raise HTTPException(status_code=400, detail=f'Invalid action_type: {action_type}')
        
        # Get all matching records (no pagination for export)
        records = financial_audit_service.get_audit_trail(
            db=db,
            payment_id=payment_id,
            invoice_id=invoice_id,
            booking_id=booking_id,
            action_type=action_type_enum,
            user_id=user_id,
            start_date=start,
            end_date=end,
            limit=10000,  # Large limit for export
            offset=0
        )
        
        # Log export action to audit trail
        try:
            financial_audit_service.log_financial_action(
                db=db,
                action_type=FinancialActionType.data_exported,
                performed_by=current_user.id,
                action_description=f'Audit trail exported as {format.upper()}',
                metadata={
                    'export_type': 'audit_trail',
                    'format': format,
                    'filters': {
                        'payment_id': payment_id,
                        'invoice_id': invoice_id,
                        'booking_id': booking_id,
                        'action_type': action_type,
                        'user_id': user_id,
                        'start_date': start_date,
                        'end_date': end_date
                    },
                    'record_count': len(records),
                    'ip_address': client_ip,
                    'user_agent': user_agent,
                    'request_id': request_id
                },
                notes=f'Audit trail export by {current_user.email}'
            )
        except Exception as e:
            logger.warning(f'Failed to log audit trail export: {e}')
        
        if format == 'csv':
            # Generate CSV
            output = io.StringIO()
            writer = csv.DictWriter(output, fieldnames=[
                'id', 'action_type', 'action_description', 'payment_id', 'invoice_id',
                'booking_id', 'amount', 'previous_amount', 'currency', 'performed_by',
                'performed_by_email', 'notes', 'created_at', 'metadata'
            ])
            writer.writeheader()
            
            for record in records:
                writer.writerow({
                    'id': record.id,
                    'action_type': record.action_type.value,
                    'action_description': record.action_description,
                    'payment_id': record.payment_id or '',
                    'invoice_id': record.invoice_id or '',
                    'booking_id': record.booking_id or '',
                    'amount': float(record.amount) if record.amount else '',
                    'previous_amount': float(record.previous_amount) if record.previous_amount else '',
                    'currency': record.currency or '',
                    'performed_by': record.performed_by,
                    'performed_by_email': record.performed_by_email or '',
                    'notes': record.notes or '',
                    'created_at': record.created_at.isoformat() if record.created_at else '',
                    'metadata': json.dumps(record.audit_metadata) if record.audit_metadata else ''
                })
            
            date_str = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
            return Response(
                content=output.getvalue(),
                media_type='text/csv',
                headers={
                    'Content-Disposition': f'attachment; filename="audit_trail_{date_str}.csv"'
                }
            )
        else:
            # Generate JSON
            export_data = []
            for record in records:
                export_data.append({
                    'id': record.id,
                    'action_type': record.action_type.value,
                    'action_description': record.action_description,
                    'payment_id': record.payment_id,
                    'invoice_id': record.invoice_id,
                    'booking_id': record.booking_id,
                    'amount': float(record.amount) if record.amount else None,
                    'previous_amount': float(record.previous_amount) if record.previous_amount else None,
                    'currency': record.currency,
                    'performed_by': record.performed_by,
                    'performed_by_email': record.performed_by_email,
                    'metadata': record.audit_metadata,
                    'notes': record.notes,
                    'created_at': record.created_at.isoformat() if record.created_at else None
                })
            
            date_str = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
            return Response(
                content=json.dumps(export_data, indent=2),
                media_type='application/json',
                headers={
                    'Content-Disposition': f'attachment; filename="audit_trail_{date_str}.json"'
                }
            )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error exporting audit trail: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.post('/retention/cleanup')
async def cleanup_old_audit_records(
    request: Request,
    retention_days: int = Query(2555, ge=365, le=3650),  # Default 7 years, min 1 year, max 10 years
    current_user: User = Depends(authorize_roles('admin')),
    db: Session = Depends(get_db)
):
    """
    Clean up audit trail records older than retention period.
    WARNING: This is a destructive operation. Only admins can perform this.
    """
    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:
        cutoff_date = datetime.utcnow() - timedelta(days=retention_days)
        
        # Count records to be deleted
        records_to_delete = db.query(FinancialAuditTrail).filter(
            FinancialAuditTrail.created_at < cutoff_date
        ).count()
        
        if records_to_delete == 0:
            return success_response(
                data={'deleted_count': 0, 'cutoff_date': cutoff_date.isoformat()},
                message='No records found older than retention period'
            )
        
        # Log cleanup action before deletion
        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'Audit trail cleanup: {records_to_delete} records older than {retention_days} days',
                metadata={
                    'action': 'audit_cleanup',
                    'retention_days': retention_days,
                    'cutoff_date': cutoff_date.isoformat(),
                    'records_deleted': records_to_delete,
                    'ip_address': client_ip,
                    'user_agent': user_agent,
                    'request_id': request_id
                },
                notes=f'Audit trail cleanup initiated by {current_user.email}'
            )
        except Exception as e:
            logger.warning(f'Failed to log audit cleanup action: {e}')
        
        # Delete old records
        deleted_count = db.query(FinancialAuditTrail).filter(
            FinancialAuditTrail.created_at < cutoff_date
        ).delete(synchronize_session=False)
        
        db.commit()
        
        return success_response(
            data={
                'deleted_count': deleted_count,
                'cutoff_date': cutoff_date.isoformat(),
                'retention_days': retention_days
            },
            message=f'Successfully deleted {deleted_count} audit trail records older than {retention_days} days'
        )
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error cleaning up audit trail: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get('/retention/stats')
async def get_retention_stats(
    retention_days: int = Query(2555, ge=365, le=3650),
    current_user: User = Depends(authorize_financial_access('admin', 'accountant')),
    db: Session = Depends(get_db)
):
    """Get statistics about audit trail retention."""
    try:
        cutoff_date = datetime.utcnow() - timedelta(days=retention_days)
        
        total_records = db.query(FinancialAuditTrail).count()
        records_to_delete = db.query(FinancialAuditTrail).filter(
            FinancialAuditTrail.created_at < cutoff_date
        ).count()
        records_to_keep = total_records - records_to_delete
        
        oldest_record = db.query(FinancialAuditTrail).order_by(
            FinancialAuditTrail.created_at.asc()
        ).first()
        
        newest_record = db.query(FinancialAuditTrail).order_by(
            FinancialAuditTrail.created_at.desc()
        ).first()
        
        return success_response(data={
            'retention_days': retention_days,
            'cutoff_date': cutoff_date.isoformat(),
            'total_records': total_records,
            'records_to_keep': records_to_keep,
            'records_to_delete': records_to_delete,
            'oldest_record_date': oldest_record.created_at.isoformat() if oldest_record else None,
            'newest_record_date': newest_record.created_at.isoformat() if newest_record else None
        })
    except Exception as e:
        logger.error(f'Error getting retention stats: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

