"""
 atm.py

  Write a program to simulate an ATM machine.
  Store balances and account info in a file.
  Use objects.

  --- initial thinking ---

  started in class May 3

       put information in a file named '_atm_data_.csv'

       name, username, password, checking_balance, savings_balance
       name2, username2, pass2, check2, save2
       ...

  initial screen 
  
      ***************
      * IntroCS ATM *
      ***************

      Type your name to start your login :

      What do you want to do?
         1. Withdraw cash
         2. Transfer money
         3. See accounts

 -----------------------------------

 Sun May 9 refinements to show working code  : 

   - completed the user interaction logic :

          login_screen
              user_interaction screen
                   account actions : balances, deposit, withdraw, transfer

   - dropped Account class for simplicity
   - added DataFile class to put the file input/output in one place
   - moved account_name & amount into a dictionary
   - changed datafilename to have .csv (more appropriate) extension
   - moved prompt strings to global variables, 
     in an attempt to make the left margins more consistent
     and keep the code cleaner
   - bunch of debugging
  
Jim Mahoney | cs.bennington.college | May 2021 | MIT License
"""

# input from terminal without echoing what is typed;
# see https://docs.python.org/3/library/getpass.html
from getpass import getpass  

debug = False     # turn extra debug printing on or off

datafilename = '_atm_data_.csv'

atm_prompt = """
      ***************
      * IntroCS ATM *
      *************** """

username_prompt = """
      What is your username? """

password_prompt = """      What is your password? """

invalid_user_message = """
      That username or password is invalid. Please try again. """

session_prompt = """
      Hello {}! What would you like to do?
        1  balances
        2  deposit
        3  withdraw
        4  transfer
        5  exit
"""

choice_prompt = """
      Your choice (1 to 5) is : """

amount_prompt = """
      Amount is ? """

which_account_prompt = """
      {} which account, checking (1) or savings (2) ? """

def choose_account(prompt):
    """ Ask user for account, starting question with given prompt.
        Return 'checking' or 'savings' """
    while True:
        choice = input(which_account_prompt.format(prompt))
        if int(choice) == 1:
            return 'checking'
        if int(choice) == 2:
            return 'savings'
        print("    Invalid response; please try again. ")

def choose_amount():
    """ Return an amount of money entered by user """
    while True:
        try:
            amount = float(input(amount_prompt))
            return amount
        except ValueError:
            print("    You entered an invalid amount. Please try again.")
        
class User:
    """ A person with atm account balances """
    
    def __init__(self, fullname, username, password):
        self.fullname = fullname
        self.username = username  
        self.password = password
        self.accounts = {}  # dictionary {'checking': amount} ; will set later.

    def set_account(self, account_name, amount):
        """ setup a named (checking or savings) account """
        self.accounts[account_name] = amount
        
    def is_valid(self, username, password):
        """ Return True if this user has this name and password """
        if debug:
            print("# is_valid debug ")
            print(f"#   testing username='{username}' password='{password}'")
            print(f"#   actual  username='{self.username}' " +
                  f"password='{self.password}'")
        return self.username == username and self.password == password
        
    def __str__(self):
        return f"<User '{self.fullname}' accounts {self.accounts}>"

class DataFile:
    """ read and write users to a file """
    # file format is lines 'name,password,checking_amount,savings_amount'
    # for example 'John Smith,jsmith,fakepassword,100.00,500.00'
    
    def __init__(self, filename):
        self.filename = filename
        
    def read_users(self):
        """ Read and return list of Users defined by contents of data file """
        datafile = open(self.filename)
        users = []
        for line in datafile:
            (fullname, username, password, checking, savings) = line.split(',')
            user = User(fullname, username, password)
            user.set_account('checking', float(checking))
            user.set_account('savings', float(savings))
            users.append(user)
        datafile.close()
        return users
    
    def write_users(self, users):
        """ Write each User to the data file """
        datafile = open(self.filename, 'w')
        for user in users:
            datafile.write(f"{user.fullname},{user.username},{user.password},")
            # google "python format 2 decimal digits" to find formatting
            # of a float 2.34 to a string "2.34", namely ':.24'
            checking = user.accounts['checking']
            savings = user.accounts['savings']
            datafile.write(f"{checking:.2f},{savings:.2f}")
            datafile.write("\n")  # newline character at end of line
        datafile.close()
    
class ATM:

    """ An ATM machine """
    
    def __init__(self):
        self.datafile = DataFile(datafilename)
        self.users = self.datafile.read_users()
    
    def __str__(self):
        # For debugging
        result = "--- ATM " + '-'*56
        for user in self.users:
            result = result + '\n ' + str(user)
        return result + '\n' + '-'*64

    def run(self):
        """ main interactive loop """
        while True:
            user = self.login()
            self.user_session(user)

    def login(self):
        """ Display ATM information and prompt for user info.
            Return User if valid information is input.
            If not valid info, loop and try again. """
        while True:
            print(atm_prompt)
            username = input(username_prompt)
            password = getpass(password_prompt)
            if debug:
                print(f"# login debug:")
                print(f"#   username='{username}'")
                print(f"#   password='{password}'")
            for user in self.users:
                if user.is_valid(username, password):
                    return user
            print(invalid_user_message)
            
    def user_session(self, user):
        """ Let this user make changes to their accounts; save changes """
        
        # give choices for what that user can do
        #    1 show balances
        #    2 withdraw
        #    3 deposit
        #    4 transfer
        #    5 finished

        while True:
            print(session_prompt.format(user.fullname))
            try:
                choice = int(input(choice_prompt))          # PUT ERROR HERE
                assert 1 <= choice <= 5
            except (ValueError, AssertionError):
                print ("     That is not a valid choice. Please try again. ")
                continue
            if choice == 1:
                print()
                for which in ('checking', 'savings'):
                    balance = user.accounts[which]
                    acct_name = which.title()
                    print(f"      {acct_name} balance is {balance:.2f}.")
            elif choice == 2:
                which = atm.choose_account('Deposit to')
                amount = choose_amount()
                user.accounts[which] = user.accounts[which] + amount
            elif choice == 3:
                which = choose_account('Withdraw from')
                amount = choose_amount()
                user.accounts[which] = user.accounts[which] - amount
            elif choice == 4:
                from_account = choose_account('Transfer from')
                to_account = choose_account('Transfer to')
                amount = choose_amount()                
            elif choice == 5:
                print("\n      Saving account changes.")
                self.datafile.write_users(self.users)
                return

def main():
    atm = ATM()
    if debug: print(atm)
    atm.run()

if __name__ == '__main__':
    main()