from subprocess import run as subrun
import json
import random
import string
from datetime import datetime

def run(command):
    com = subrun(command, shell=True, capture_output=True)
    if com.returncode != 0:
        print(com.stdout.decode('ascii'))
        raise Exception(com.stderr.decode('ascii'))
    return com

def main():
    print("Do you want to create a new project or use an existing one?")
    print("1. Create a new project")
    print("2. Use an existing project")
    choice = input("Enter your choice: ")
    if choice == "1":
        createNewProject()
    elif choice == "2":
        useExistingProject()
    else:
        print("Invalid choice.. Please try again.")
        main()

def createNewProject():
    print("Type the name of your Bubble project. It may take up to a minute to create.")
    users_project_name = input("Type a project prefix and press enter: ")
    users_project_name = users_project_name.strip().replace(' ', '-').lower()
    if users_project_name == '':
        users_project_name = 'bubble-dz'
        proj_name = f"{users_project_name}-" + ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
        print(f"Creating project {proj_name} ...")
        try:
            run(f"gcloud projects create {proj_name}")
            run("gcloud config set project " + proj_name)
            print(f"Project {proj_name} created successfully!")
            linkBillingAccount(proj_name, newpro=True)
        except Exception as e:
            print(e)
            print("Project creation failed. Please try again.")
            createNewProject()
    else:
        if len(users_project_name) < 6 or len(users_project_name) > 23:
            print("Project prefix must be between 6 and 23 characters.")
            createNewProject()
        else:
            proj_name = f"{users_project_name}-" + ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
            print(f"Creating project {proj_name} ...")
            try:
                run(f"gcloud projects create {proj_name}")
                run("gcloud config set project " + proj_name)
                print(f"Project {proj_name} created successfully!")
                linkBillingAccount(proj_name, newpro=True)
            except Exception as e:
                print(e)
                print("Project creation failed. Please try again.")
                createNewProject()

def useExistingProject():
    get_pro = run("gcloud projects list --format=json")
    pros = json.loads(get_pro.stdout)
    if len(pros) == 0:
        print("No projects found. Creating a new one ...")
        createNewProject()
    else:
        for i in range(len(pros)):
            pros[i]["id"] = i
        for p in pros:
            print(f"{p['id']}. {p['projectId']}")
        proj_name = input("Enter the project name: ")
        # it must be a number and also exist in the list
        if not proj_name.isdigit():
            print("Invalid choice.. Please try again.")
            useExistingProject()
        proj_name = int(proj_name)
        if proj_name < 0 or proj_name >= len(pros):
            print("Invalid choice.. Please try again.")
            useExistingProject()
        proj_name = pros[proj_name]["projectId"]
        run("gcloud config set project " + proj_name)
        print(f"Using project {proj_name} ...")
        linkBillingAccount(proj_name, newpro=False)

def linkBillingAccount(proj_name, newpro):
    get_bil = run("gcloud beta billing accounts list --format=json")
    bacs = json.loads(get_bil.stdout)
    active_bac = ""
    for b in bacs:
        if b["open"]:
            active_bac = b["name"]
            break
    if active_bac == "":
        raise Exception("ERROR: No active billing accounts. Activate your trial and try agian. Even after your trial expires you will be able to use 5GB/month of storage for free. However, if your trial has expired make sure you still have a valid payment method (Search for Payment Method in the search box on the top) to use your free tier.")
    active_bac = active_bac.split('/')[1]
    run(f"gcloud beta billing projects link {proj_name} --billing-account={active_bac}")
    if newpro:
        createNewServiceAccount(proj_name)
    else:
        ServiceSelection(proj_name)

def ServiceSelection(proj_name):
    print("Do you want to create a new service account or use an existing one?")
    print("1. Create a new service account")
    print("2. Use an existing service account")
    choice = input("Enter your choice: ")
    if choice == "1":
        createNewServiceAccount(proj_name)
    elif choice == "2":
        useExistingServiceAccount(proj_name)
    else:
        print("Invalid choice.. Please try again.")
        ServiceSelection(proj_name)

def createNewServiceAccount(proj_name):
    # create new account
    accountname = input("Type a name for your service account and press enter: ")
    accountname = accountname if accountname != '' else 'bubble-dz'
    # check lenght must be between 6 and 30 characters
    if len(accountname) < 6 or len(accountname) > 30:
        print("Service account name must be between 6 and 30 characters.")
        createNewServiceAccount(proj_name)
    else:
        accountname = accountname.strip().replace(' ', '-').lower()
        if checkAccount(accountname):
            print("That account name is already taken. Please try again.")
            createNewServiceAccount(proj_name)
        else:
            run(f"gcloud iam service-accounts create {accountname} --project {proj_name}")
            print(f"Service account {accountname} created successfully!")
            # bind roles
            run(f'gcloud projects add-iam-policy-binding {proj_name} --member="serviceAccount:{accountname}@{proj_name}.iam.gserviceaccount.com" --role="roles/storage.admin"')
            # generate key
            print(f"Creating keys ...")
            timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
            filename = f"dropzone_credentials_{timestamp}.txt"
            run(f'gcloud iam service-accounts keys create {filename} --iam-account={accountname}@{proj_name}.iam.gserviceaccount.com')
            print(f"Keys created successfully!")
            bucketSelection(proj_name,filename)

def checkAccount(accountname):
        get_acc = run("gcloud iam service-accounts list --format=json")
        accs = json.loads(get_acc.stdout)
        for a in accs:
            email = a["email"]
            if email.split('@')[0] == accountname:
                return True
        return False

def useExistingServiceAccount(proj_name):
    get_accounts = run(f"gcloud iam service-accounts list --project {proj_name} --format=json")
    accounts = json.loads(get_accounts.stdout)
    if len(accounts) == 0:
        print("No service accounts found. Please create a new one ...")
        useExistingServiceAccount(proj_name)
    else:
        for i in range(len(accounts)):
            accounts[i]["id"] = i
        for a in accounts:
            print(f"{a['id']}. {a['email']}")
        acc_name = input("Enter the service account name: ")
        # it must be a number and also exist in the list
        if not acc_name.isdigit():
            print("Invalid choice.. Please try again.")
            useExistingServiceAccount(proj_name)
        acc_name = int(acc_name)
        if acc_name < 0 or acc_name >= len(accounts):
            print("Invalid choice.. Please try again.")
            useExistingServiceAccount(proj_name)
        acc_name = accounts[acc_name]["email"]
        # bind roles
        run(f'gcloud projects add-iam-policy-binding {proj_name} --member="serviceAccount:{acc_name}" --role="roles/storage.admin"')        
        # generate key
        print(f"Creating keys ...")
        timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
        filename = f"dropzone_credentials_{timestamp}.txt"
        run(f'gcloud iam service-accounts keys create {filename} --iam-account={acc_name}')
        print(f"Keys created successfully!")
        bucketSelection(proj_name,filename)

def bucketSelection(proj_name,filename):
    print("Do you want to create a new bucket or use an existing one?")
    print("1. Create a new bucket")
    print("2. Use an existing bucket")
    choice = input("Enter your choice: ")
    if choice == "1":
        createNewBucket(proj_name,filename)
    elif choice == "2":
        useExistingBucket(proj_name,filename)
    else:
        print("Invalid choice.. please try again.")
        bucketSelection(proj_name,filename)

def createNewBucket(proj_name,filename):
    bucketname = input("Type a name for your bucket and press enter: ")
    bucketname = bucketname if bucketname != '' else 'bubble'+ "-" + ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
    # check lenght must be between 6 and 23 characters
    if len(bucketname) < 6 or len(bucketname) > 23:
        print("Bucket name must be between 6 and 23 characters.")
        createNewBucket(proj_name,filename)
    else:
        bucketname = bucketname.strip().replace(' ', '-').lower()
        # add random string to the end
        bucketname = bucketname + "-" + ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
        # create new bucket
        print(f"Creating bucket {bucketname} ...")
        run(f"gsutil mb -p {proj_name} -b on gs://{bucketname}")
        print(f"Bucket {bucketname} created successfully!")
        print(f"Setting up CORS & Fined-Grained ACL for bucket {bucketname} ...")
        with open('bubble-cors.json', 'w') as f:
            f.write('[{"origin":["*"],"responseHeader":["X-Requested-With","Access-Control-Allow-Origin","Content-Type","Content-Length","Content-Range","Location","Range","X-Goog-Resumable","X-Upload-Content-Type","X-Upload-Content-Length"],"method":["GET","POST","HEAD","PUT","DELETE","OPTIONS"],"maxAgeSeconds":3600}]')
        run(f'gsutil cors set bubble-cors.json gs://{bucketname}')
        run(f'gsutil uniformbucketlevelaccess set off gs://{bucketname}')
        print(f"Your bucket name is: {bucketname}")
        print(f"Also {filename} will be downloaded to your machine. Open the private key JSON file with a text editor, copy-paste the following parameters from your file to the Plugin settings: CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY in plugin settings.")
        downloadCredentials(filename)

def useExistingBucket(proj_name,filename):
    # get list of buckets
    bucketlist = []
    get_buckets = run(f"gsutil ls -p {proj_name}")
    allbuckets_str = get_buckets.stdout.decode("utf-8")
    bucketlist = allbuckets_str.split('\n')
    bucketlist.pop()
    if len(bucketlist) == 0:
        print("No buckets found. Creating a new one ...")
        createNewBucket(proj_name,filename)
    else:
        for i in range(len(bucketlist)):
            bucketlist[i] = bucketlist[i].split('gs://')[1]
            bucketlist[i] = bucketlist[i].split('/')[0]
            print(f"{i}. {bucketlist[i]}")
        bucket = input("Enter the bucket name: ")
        # number must exist in the list and it must be a number
        if not bucket.isdigit():
            print("Invalid choice.. Please try again.")
            useExistingBucket(proj_name,filename)
        else:
            bucket = int(bucket)
            bucket = bucketlist[bucket]
            print(f"Using bucket {bucket} ...")
            print(f"Setting up CORS & Fined-Grained ACL for bucket {bucket} ...")
            with open('bubble-cors.json', 'w') as f:
                f.write('[{"origin":["*"],"responseHeader":["X-Requested-With","Access-Control-Allow-Origin","Content-Type","Content-Length","Content-Range","Location","Range","X-Goog-Resumable","X-Upload-Content-Type","X-Upload-Content-Length"],"method":["GET","POST","HEAD","PUT","DELETE","OPTIONS"],"maxAgeSeconds":3600}]')

            run(f'gsutil cors set bubble-cors.json gs://{bucket}')
            run(f'gsutil uniformbucketlevelaccess set off gs://{bucket}')

            print(f"Your bucket name is: {bucket}")
            print(f"Also {filename} will be downloaded to your machine. Open the private key JSON file with a text editor, copy-paste the following parameters from your file to the Plugin settings: CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY in plugin settings.")
            downloadCredentials(filename)

def downloadCredentials(filename):
    print(f"Downloading credentials ...")
    run(f'cloudshell download {filename}')
    print(f"Credentials downloaded successfully!")

if __name__ == "__main__":
    main()
