# standard imports
import logging
from typing import Tuple

# third party imports

# local imports
from cic_ussd.accounts import BalanceManager
from cic_ussd.db.models.user import AccountStatus, User
from cic_ussd.operations import get_user_by_phone_number, save_to_in_memory_ussd_session_data
from cic_ussd.state_machine.state_machine import UssdStateMachine
from cic_ussd.transactions import OutgoingTransactionProcessor


logg = logging.getLogger(__file__)


def is_valid_recipient(state_machine_data: Tuple[str, dict, User]) -> bool:
    """This function checks that a user exists, is not the initiator of the transaction, has an active account status
    and is authorized to perform  standard transactions.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: tuple
    :return: A user's validity
    :rtype: bool
    """
    user_input, ussd_session, user = state_machine_data
    recipient = get_user_by_phone_number(phone_number=user_input)
    is_not_initiator = user_input != user.phone_number
    has_active_account_status = user.get_account_status() == AccountStatus.ACTIVE.name
    logg.debug('This section requires implementation of checks for user roles and authorization status of an account.')
    return is_not_initiator and has_active_account_status


def is_valid_token_agent(state_machine_data: Tuple[str, dict, User]) -> bool:
    """This function checks that a user exists, is not the initiator of the transaction, has an active account status
    and is authorized to perform exchange transactions.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: tuple
    :return: A user's validity
    :rtype: bool
    """
    user_input, ussd_session, user = state_machine_data
    # is_token_agent = AccountRole.TOKEN_AGENT.value in user.get_user_roles()
    logg.debug('This section requires implementation of user roles and authorization to facilitate exchanges.')
    return is_valid_recipient(state_machine_data=state_machine_data)


def is_valid_transaction_amount(state_machine_data: Tuple[str, dict, User]) -> bool:
    """This function checks that the transaction amount provided is valid as per the criteria for the transaction
    being attempted.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: tuple
    :return: A transaction amount's validity
    :rtype: bool
    """
    user_input, ussd_session, user = state_machine_data
    try:
        return int(user_input) > 0
    except ValueError:
        return False


def has_sufficient_balance(state_machine_data: Tuple[str, dict, User]) -> bool:
    """This function checks that the transaction amount provided is valid as per the criteria for the transaction
    being attempted.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: tuple
    :return: An account balance's validity
    :rtype: bool
    """
    user_input, ussd_session, user = state_machine_data
    balance_manager = BalanceManager(address=user.blockchain_address,
                                     chain_str=UssdStateMachine.chain_str,
                                     token_symbol='SRF')
    balance = balance_manager.get_operational_balance()
    return int(user_input) <= balance


def save_recipient_phone_to_session_data(state_machine_data: Tuple[str, dict, User]):
    """This function saves the phone number corresponding the intended recipients blockchain account.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: str
    """
    user_input, ussd_session, user = state_machine_data
    session_data = {
        'recipient_phone_number': user_input
    }
    save_to_in_memory_ussd_session_data(queue='cic-ussd', session_data=session_data, ussd_session=ussd_session)


def save_transaction_amount_to_session_data(state_machine_data: Tuple[str, dict, User]):
    """This function saves the phone number corresponding the intended recipients blockchain account.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: str
    """
    user_input, ussd_session, user = state_machine_data
    session_data = {
        'transaction_amount': user_input
    }
    save_to_in_memory_ussd_session_data(queue='cic-ussd', session_data=session_data, ussd_session=ussd_session)


def process_transaction_request(state_machine_data: Tuple[str, dict, User]):
    """This function saves the phone number corresponding the intended recipients blockchain account.
    :param state_machine_data: A tuple containing user input, a ussd session and user object.
    :type state_machine_data: str
    """
    user_input, ussd_session, user = state_machine_data

    # get user from phone number
    recipient_phone_number = ussd_session.get('session_data').get('recipient_phone_number')
    recipient = get_user_by_phone_number(phone_number=recipient_phone_number)
    to_address = recipient.blockchain_address
    from_address = user.blockchain_address
    amount = int(ussd_session.get('session_data').get('transaction_amount'))
    outgoing_tx_processor = OutgoingTransactionProcessor(chain_str=UssdStateMachine.chain_str,
                                                         from_address=from_address,
                                                         to_address=to_address)
    outgoing_tx_processor.process_outgoing_transfer_transaction(amount=amount)