"""Lambda process function module.""" from typing import Any, Dict from lambdacommon.aws.msk import msk_handler import pydantic from config import app_logger as logger from src import ( constants, kafka, ows_abacus_account, ows_abacus_state, ows_payee, ) from src.exceptions import LogicError, OwsAbacusAccountException, OwsPayeeException from src.models import AccountPayee, Event @msk_handler() # type: ignore def handler(event: Dict[str, Any], context: Any) -> None: """Lambda entry point.""" logger.info('Received event', extra={'event': event}) try: valid_event = Event.model_validate(event) except pydantic.ValidationError as exc: logger.error(f'Invalid event format: {exc.errors()}') return process_event(valid_event) def process_event(event: Event) -> None: """Process event.""" logger.info(f'Processing event: {event}') account_payee = event.account_payee try: previous_status_data = ows_payee.get_payoneer_payee_status( account_payee.account_payee_id, account_payee.prev_payoneer_program_id ) except OwsPayeeException as e: logger.error(f'Error on get status: {e}') return if previous_status_data.status.type != constants.PayoneerStatusTypes.ACTIVE: logger.error(f'Inactive payee status: {previous_status_data}') return try: move_type = ows_abacus_account.get_payoneer_program_move_type( account_payee.prev_payoneer_program_id, account_payee.payoneer_program_id, ) except OwsAbacusAccountException as e: logger.error(f'Error on get move type: {e}') return match move_type: case constants.PayoneerProgramMoveTypes.ADOPT_AND_RELEASE: _adopt_and_release_strategy(account_payee) case constants.PayoneerProgramMoveTypes.RELEASE_AND_WARN: _release_and_warn_strategy(account_payee) case constants.PayoneerProgramMoveTypes.LEGACY: _legacy_strategy(account_payee) case _: raise LogicError(f'Unknown move type: {move_type}') kafka.emit_resync_payment_readiness_event(account_payee.account_payee_id) def _adopt_and_release_strategy(account_payee: AccountPayee) -> None: """Adopt and release strategy.""" logger.info('Using adopt and release strategy for program move.') try: _release_from_new_program(account_payee) except OwsPayeeException as e: logger.error( f'Failed to release payee {account_payee.account_payee_id} ' f'from new Payoneer program {account_payee.payoneer_program_id} before move: {e}.' ) ows_abacus_state.set_payment_eligibility_error( account_payee.account_payee_id, constants.MOVE_ERROR_MSG ) return try: program_move_data = ows_payee.move_payoneer_program( account_payee.account_payee_id, account_payee.prev_payoneer_program_id, account_payee.payoneer_program_id, ) except OwsPayeeException as e: logger.error(f'Error on move program: {e}') ows_abacus_state.set_payment_eligibility_error( account_payee.account_payee_id, constants.MOVE_ERROR_MSG ) return logger.info(f'Program move result: {program_move_data}') if ows_abacus_state.banking_details_rejected(account_payee.account_payee_id): logger.info('Resetting rejected banking states') ows_payee.reset_banking_states(account_payee.account_payee_id) _release_from_old_program(account_payee) def _release_and_warn_strategy(account_payee: AccountPayee) -> None: """Release and warn strategy.""" logger.info('Using release and warn strategy for program move.') ows_payee.reset_banking_states(account_payee.account_payee_id) _release_from_old_program(account_payee) def _legacy_strategy(account_payee: AccountPayee) -> None: """Legacy strategy.""" logger.info('Using legacy strategy for program move.') try: program_move_data = ows_payee.move_payoneer_program( account_payee.account_payee_id, account_payee.prev_payoneer_program_id, account_payee.payoneer_program_id, ) except OwsPayeeException as e: logger.error(f'Error on move program: {e}') return logger.info(f'Program move result: {program_move_data}') is_wire_program = ows_abacus_account.is_payoneer_wire_program( account_payee.payoneer_program_id ) if not is_wire_program: logger.info( f'Skipping release for payee {account_payee.account_payee_id}. ' f'New program {account_payee.payoneer_program_id} ' f'is not a payoneer_wire program.' ) return _release_from_old_program(account_payee) def _release_from_new_program(account_payee: AccountPayee) -> None: try: status_data = ows_payee.get_payoneer_payee_status( account_payee.account_payee_id, account_payee.payoneer_program_id ) except OwsPayeeException as e: # this mostly means that payee does not exist in new program logger.error(f'Error on get status for new program: {e}.') return logger.info( f'Payee {account_payee.account_payee_id} is already exists in the new program {account_payee.payoneer_program_id} ' f'with status {status_data.status}. ' f'Releasing before move.' ) ows_payee.release_payee_payoneer_program( account_payee.account_payee_id, account_payee.payoneer_program_id, ) logger.info( f'Successfully released payee {account_payee.account_payee_id} from target program ' f'to move into Payoneer wire program {account_payee.payoneer_program_id}' ) def _release_from_old_program(account_payee: AccountPayee) -> None: try: ows_payee.release_payee_payoneer_program( account_payee.account_payee_id, account_payee.prev_payoneer_program_id ) logger.info( f'Successfully released payee {account_payee.account_payee_id} ' f'to move into Payoneer wire program {account_payee.payoneer_program_id}' ) except Exception as e: logger.error( f'Failed to release payee {account_payee.account_payee_id} ' f'from previous Payoneer program: {e}. ' f'Program move was successful, but cleanup failed.' )