from subprocess import run as subrun
import json
import random
import string
from datetime import datetime
api = 'speech'

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 activateAPI(proj_name, api):
    # enable  api
    print(f'Activating {api}.googleapis.com for project {proj_name}...')
    run(f'gcloud services enable {api}.googleapis.com --project {proj_name}')

def grantIAMPermissions(proj_name, service_account_email):
    """Grant necessary IAM permissions for Speech V2 API"""
    print(f"Granting Speech API permissions to {service_account_email}...")
    
    # Grant Speech Client role for V2 API access
    try:
        run(f'gcloud projects add-iam-policy-binding {proj_name} \
            --member="serviceAccount:{service_account_email}" \
            --role="roles/speech.client" \
            --quiet')
        print("Speech Client role granted successfully!")
    except Exception as e:
        print(f"Warning: Could not grant Speech Client role: {e}")
        print("Attempting to grant Speech Editor role as fallback...")
        try:
            run(f'gcloud projects add-iam-policy-binding {proj_name} \
                --member="serviceAccount:{service_account_email}" \
                --role="roles/speech.editor" \
                --quiet')
            print("Speech Editor role granted successfully!")
        except Exception as e2:
            print(f"Warning: Could not grant Speech Editor role: {e2}")
            print("You may need to manually grant permissions in the Google Cloud Console.")

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'
        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 number: ")
        # 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}")
    activateAPI(proj_name, api)
    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'
    # 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!")
            
            # Grant IAM permissions for the new service account
            service_account_email = f"{accountname}@{proj_name}.iam.gserviceaccount.com"
            grantIAMPermissions(proj_name, service_account_email)
            
            # generate key
            print(f"Creating keys ...")
            timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
            filename = f"{api}_sync_only_credentials_{timestamp}.txt"
            run(f'gcloud iam service-accounts keys create {filename} --iam-account={service_account_email}')
            print(f"Keys created successfully!")
            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 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 ...")
        createNewServiceAccount(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 number: ")
        # 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"]
        
        # Grant IAM permissions for the existing service account
        print("\nGranting necessary permissions for Speech V2 API...")
        print("This may take a moment...")
        grantIAMPermissions(proj_name, acc_name)
        
        # generate key
        print(f"Creating keys ...")
        timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
        filename = f"{api}_sync_only_credentials_{timestamp}.txt"
        run(f'gcloud iam service-accounts keys create {filename} --iam-account={acc_name}')
        print(f"Keys created successfully!")
        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()