# handles interactions with JSON files
import json
from . import crypto_utils
from datetime import date
import os

# admin + user DB JSON initialization
def init_admin_DB(admin_username, admin_pwrd):
    crypto_text = crypto_utils.hash_admin_creds(admin_pwrd)     
    DB ={ "username": admin_username,
         "ciphertext": crypto_text
        }
    
    with open(f"{admin_username}.json", "w") as f:
        json.dump(DB, f)
    print(f"New admin: {admin_username} successfully added. Vault initialized and ready for use.")
def init_user_DB(admin_username, admin_pwrd): 
    empty_entries_DB = {"entries": []}
    entries_json = json.dumps(empty_entries_DB)
    
    dat = date.today()
    year = dat.year
    
    DB_salt = crypto_utils.generate_salt()
    DB_key = crypto_utils.derive_key(admin_pwrd, DB_salt)

    entries = crypto_utils.encrypt(DB_key, entries_json)
    DB_key = None
    
    DB= {"vault_ID": f"{admin_username}_vault_{year}",
         "kdf": {"algo": "argon2_id",
                 "salt_base64": DB_salt.decode("utf-8"),
                 "time": 3,
                 "memory": 65536,
                 "parallelism": 4,
                 "hash_length": 32},
        "contents": entries.decode()
    }
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB,f)

# user <--> DB interactions
def add_creds(admin_username, admin_pwrd, new_entries: dict): 
    # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    salt = kdf["salt_base64"]
    salt = salt.encode("utf-8")
    
    key = crypto_utils.derive_key(admin_pwrd, salt)
    entries_plaintext = crypto_utils.decrypt(key, entries)
    entries_plaintext = entries_plaintext.decode()
    entries_plaintext= json.loads(entries_plaintext)
    
    # adding the new credentials to the DB
    entries_list = entries_plaintext["entries"]
    entries_list.append(new_entries)
    
    # re-encrypting the user DB entries
    entries_plaintext = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(key, entries_plaintext)
    key = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)
    print("New credentials successfully added!")

def delete_creds(admin_username, admin_pwrd, entry_ID):
   # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    salt = kdf["salt_base64"]
    salt = salt.encode("utf-8")
    
    key = crypto_utils.derive_key(admin_pwrd, salt)
    entries_plaintext = crypto_utils.decrypt(key, entries)
    entries_plaintext = entries_plaintext.decode()
    entries_plaintext= json.loads(entries_plaintext)
    
    # deleting an entry based on the ID(corresponds to list index)
    entries_list = entries_plaintext["entries"]
    entries_list.pop(entry_ID)
    
    # re-encrypting the user DB entries
    entries_json = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(key, entries_json)
    key = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)
    print(f"The credentials of entry {entry_ID} were successfully deleted.")

def edit_creds(admin_username, admin_pwrd, field, newinfo, entry_ID):
    # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    salt = kdf["salt_base64"]
    salt = salt.encode("utf-8")
    
    DBkey = crypto_utils.derive_key(admin_pwrd, salt)
    entries_plaintext = crypto_utils.decrypt(DBkey, entries)
    entries_plaintext = entries_plaintext.decode()
    entries_plaintext= json.loads(entries_plaintext) 

    # updating the specified credentials with new info
    entries_list = entries_plaintext["entries"]
    entry = entries_list[entry_ID] # note: entry is a dictionary
    
    if field == "password":
            salt = crypto_utils.generate_salt()
            key = crypto_utils.derive_key(admin_pwrd, salt)
            token = crypto_utils.encrypt(key, newinfo)
            key = None
            
            entry["password"] = token.decode()
            entry["salt"] = salt.decode()
    else:
        entry[field] = newinfo
    
    # re-encrypting the user DB entries
    entries_json = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(DBkey, entries_json)
    DBkey = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)
    print(f"The {field} of entry {entry_ID} was successfully updated.")

def list_creds(admin_username, admin_pwrd):
    # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    salt = kdf["salt_base64"]
    salt = salt.encode("utf-8")
    
    key = crypto_utils.derive_key(admin_pwrd, salt)
    entries_plaintext = crypto_utils.decrypt(key, entries)
    entries_plaintext = entries_plaintext.decode()
    entries_plaintext= json.loads(entries_plaintext)
    
    # printing the creds from the database
    entries_list = entries_plaintext["entries"]
    if len(entries_list) ==0:
        print("The vault is empty.")
    else:
        print("Credentials:")
        for i in entries_list:
            print(f"""\nEntry_ID: {entries_list.index(i)}
Website: {i["website"]}
Username: {i["username"]}
Password: {"*"*7}""") # arbitrary value
    
    # re-encrypting the user DB entries
    entries_json = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(key, entries_json)
    key = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)

def view_pwrd(admin_username, admin_pwrd, entry_ID):
    # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    DBsalt = kdf["salt_base64"]
    DBsalt = DBsalt.encode("utf-8") 
    
    DBkey = crypto_utils.derive_key(admin_pwrd, DBsalt)
    entries_plaintext = crypto_utils.decrypt(DBkey, entries)
    entries_plaintext = entries_plaintext.decode()
    entries_plaintext= json.loads(entries_plaintext)
    
    # accessing specified entry
    entries_list = entries_plaintext["entries"]
    entry = entries_list[entry_ID]
    
    # decrypting the password
    password_token = entry["password"]
    salt = entry["salt"]
    salt = salt.encode("utf-8")
    key = crypto_utils.derive_key(admin_pwrd, salt)
    password = crypto_utils.decrypt(key, password_token)
    key = None
    
    # printing the information
    print(f"""\nEntry_ID: {entry_ID}
Website: {entry["website"]}
Username: {entry["username"]}
Password: {password.decode()}""") 
    
    password = None #bye-bye reference :)
    
    # re-encrypting the user DB entries
    entries_json = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(DBkey, entries_json)
    DBkey = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)

def reset_DB(admin_username, admin_pwrd):
    # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    salt = kdf["salt_base64"]
    salt = salt.encode("utf-8")
    
    key = crypto_utils.derive_key(admin_pwrd, salt)
    entries_plaintext = crypto_utils.decrypt(key, entries)
    entries_plaintext = entries_plaintext.decode()
    
    # overwriting the database as empty
    entries_plaintext= {"entries":[]}
    
    # re-encrypting the user DB entries
    entries_json = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(key, entries_json)
    key = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)
    print(f"{admin_username}'s vault has been reset.")

def list_by_site(admin_username, admin_pwrd, site):
    # decrypting the database
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    entries = DB_header["contents"]
    kdf = DB_header["kdf"]
    salt = kdf["salt_base64"]
    salt = salt.encode("utf-8")
    
    key = crypto_utils.derive_key(admin_pwrd, salt)
    entries_plaintext = crypto_utils.decrypt(key, entries)
    entries_plaintext = entries_plaintext.decode()
    entries_plaintext= json.loads(entries_plaintext)
    
    #printing credentials based on desired website
    entries_list = entries_plaintext["entries"]
    site_list = []
    for i in entries_list:
        if i["website"] == site:
            site_list.append(i)
    if len(site_list)==0:
        print(f"There are no saved credentials for {site}")
    else:
        for i in site_list:
                print(f"""\nEntry_ID: {entries_list.index(i)}
    Website: {i["website"]}
    Username: {i["username"]}
    Password: {"*"*7}""")
        
    # re-encrypting the user DB entries
    entries_json = json.dumps(entries_plaintext)
    entries_token = crypto_utils.encrypt(key, entries_json)
    key = None
    DB_header["contents"] = entries_token.decode()
    
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)
    
def delete_admin(admin_username):
    # sanitizing the user database before deletion
    with open(f"{admin_username}_vault.json", "r") as f:
        DB_header = json.load(f)
    
    DB_header["contents"] = None
    kdf = DB_header["kdf"]
    kdf["salt_base64"] = None
 
    with open(f"{admin_username}_vault.json", "w") as f:
        json.dump(DB_header,f)
       
    # sanitizing the admin file before deletion
    with open(f"{admin_username}.json", "r") as f:
        admin_json = json.load(f)
    
    admin_json["ciphertext"] = None
 
    with open(f"{admin_username}.json", "w") as f:
        json.dump(admin_json,f)
        
    # deleting the files
    os.remove(f"{admin_username}_vault.json")
    os.remove(f"{admin_username}.json")
    print(f"{admin_username}'s vault files have been successfully deleted.")