import random
import string
import time
import subprocess
from appium.webdriver.common.appiumby import AppiumBy
from appium import webdriver
from appium.options.android import UiAutomator2Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput

# Define total number of accounts to create
total_accounts = int(input("Enter the number of accounts to create: "))

# Set up Appium options
options = UiAutomator2Options()
options.device_name = "YourDeviceName"  # Replace with the actual device name or ID
options.app_package = "com.spotify.music"
options.app_activity = "com.spotify.music.MainActivity"
options.no_reset = True
options.automation_name = "UiAutomator2"
options.new_command_timeout = 3600
options.auto_grant_permissions = True


# Helper function to wait for an element to be present
def wait_for_element(driver, by, value, timeout=60):
    try:
        element = WebDriverWait(driver, timeout).until(EC.presence_of_element_located((by, value)))
        return element
    except TimeoutException:
        raise Exception(f"Element with locator ({by}, {value}) not found after {timeout} seconds.")

# Helper function to generate random email
def generate_random_email():
    first_names = ["Ahmed", "Aiko", "Aisha", "Akio", "Aleksandr", "Alessia", "Ali", "Amara", "Ana", "Ananya",
        "Anders", "Andre", "Angela", "Anika", "Anwar", "Aria", "Arturo", "Asher", "Ayaan", "Beatriz",
        "Benjamín", "Bo", "Boris", "Bruno", "Caoimhe", "Carlos", "Carmen", "Celine", "Cheng", "Chiara",
        "Chloe", "Chun", "Claudia", "Dafne", "Daniel", "Dario", "Davina", "Deepak", "Diego", "Dmitry",
        "Dominique", "Dung", "Edgar", "Eduardo", "Eiko", "Ekaterina", "Elif", "Elijah", "Ella", "Emily",
        "Emiliano", "Enzo", "Eoin", "Esra", "Fatima", "Felipe", "Feng", "Filip", "Flora", "Fumiko",
        "Gabriel", "Gaia", "George", "Gianni", "Giovanni", "Gonzalo", "Habib", "Hajime", "Hana", "Hanan",
        "Hans", "Haruto", "Hassan", "Helena", "Hideki", "Hugo", "Ibrahima", "Imran", "Ines", "Isaac",
        "Isabel", "Ivan", "Izumi", "Jacob", "Jamal", "Jana", "Janusz", "Javier", "Jing", "Joao",
        "Jose", "Juan", "Julia", "Julio", "Jun", "Kai", "Kalpana", "Kamal", "Karim", "Katerina",
        "Keiko", "Kenzo", "Khaled", "Kiara", "Kim", "Koji", "Konstantin", "Krzysztof", "Leandro", "Leon",
        "Liam", "Lina", "Ling", "Livia", "Lorena", "Luca", "Ludmila", "Luis", "Luz", "Mads",
        "Magdalena", "Mahdi", "Makoto", "Malika", "Manuel", "Marcelo", "Maria", "Mario", "Marisol", "Marta",
        "Martina", "Mateusz", "Mauricio", "Maya", "Mei", "Mehmet", "Miguel", "Mila", "Min", "Mircea",
        "Mohamed", "Mohammad", "Monica", "Myriam", "Nadiya", "Nala", "Nasser", "Natalia", "Nathalie", "Natsuki",
        "Nguyen", "Nia", "Nicola", "Nikita", "Nils", "Nina", "Noor", "Noriko", "Nur", "Octavio",
        "Olga", "Omar", "Oskar", "Pablo", "Padma", "Paolo", "Parvati", "Pascale", "Patrik", "Pavel",
        "Pedro", "Petra", "Pierre", "Pranav", "Priya", "Qi", "Quang", "Rafael", "Rania", "Rashid",
        "Renata", "Riccardo", "Riko", "Rita", "Rizwan", "Roza", "Ryu", "Saeed", "Saito", "Samir",
        "Sana", "Santiago", "Sara", "Sarika", "Savita", "Seong", "Shakira", "Shigeru", "Sho", "Silvio",
        "Sofía", "Soo", "Soraya", "Sulaiman", "Sumiko", "Suresh", "Svetlana", "Syed", "Tadeusz", "Takashi",
        "Tariq", "Tatiana", "Teresa", "Thiago", "Tomislav", "Tomasz", "Toru", "Tran", "Trang", "Tristan",
        "Tunde", "Umberto", "Valentina", "Valeria", "Vasilis", "Vedran", "Vera", "Victor", "Viktor", "Vinicius",
        "Vladimir", "Wan", "Wang", "Wei", "Wen", "Xia", "Ximena", "Xing", "Yamada", "Yara",
        "Yasmin", "Yasin", "Yoko", "Yoon", "Yoshiko", "Yu", "Yulia", "Yusuf", "Zainab", "Zoltan"]
    last_names = ["Abdi", "Abdullahi", "Abramovic", "Aditya", "Afolabi", "Ahmad", "Ahmed", "Aitken", "Akhtar", "Al-Hassan",
        "Al-Najjar", "Al-Sayed", "Alba", "Ali", "Alvarado", "Amadou", "Andersson", "Antonopoulos", "Araya", "Arendse",
        "Aslan", "Aung", "Babar", "Balan", "Bansal", "Barbulescu", "Bauer", "Beaulieu", "Beck", "Benitez",
        "Berger", "Bianchi", "Blanco", "Boersma", "Bose", "Bouazizi", "Brown", "Brunner", "Bui", "Buthelezi",
        "Cabral", "Cai", "Caceres", "Campos", "Carvalho", "Castro", "Ceballos", "Chan", "Chang", "Chauhan",
        "Chen", "Choi", "Christodoulou", "Chung", "Cisse", "Costa", "Coyle", "Cruz", "Dai", "Daoud",
        "Das", "De la Cruz", "Del Rosario", "Dembele", "Demir", "Díaz", "Dimitrov", "Draganovic", "Duarte", "Dumitrescu",
        "Durand", "Dutta", "Dziedzic", "Ebadi", "El-Amin", "El-Ghazzawi", "Eom", "Eriksson", "Esposito", "Fahmi",
        "Fan", "Farah", "Farkas", "Ferrer", "Fernández", "Fischer", "Fitzpatrick", "Florez", "Fuentes", "Garcia",
        "Gashi", "Gauthier", "Gavrilov", "Georgiou", "Ghosh", "Giannopoulos", "Gil", "Gonzalez", "Gorski", "Grbovic",
        "Green", "Guerrero", "Guo", "Gutierrez", "Haddad", "Haider", "Han", "Hansen", "Harris", "Hassan",
        "He", "Herrero", "Hoxha", "Huang", "Hussain", "Hwang", "Ibrahim", "Ivanov", "Jansen", "Jha",
        "Jimenez", "Johansson", "Jones", "Jovanovic", "Kaczmarek", "Khan", "Kim", "Kovacevic", "Kowalski", "Kravchenko",
        "Kumar", "Lagos", "Lam", "Larsen", "Laurent", "Lee", "Leon", "Leroy", "Li", "Liew",
        "Liu", "Lopez", "Lungu", "Machado", "Madhani", "Mahmoud", "Majic", "Mak", "Malik", "Mammadov",
        "Manzoor", "Martínez", "Mendes", "Mendoza", "Meyer", "Mirza", "Molina", "Moreira", "Moreno", "Moulin",
        "Muller", "Munteanu", "Murphy", "Mutiso", "Nair", "Ndiaye", "Ng", "Nguyen", "Nikolic", "Njoroge",
        "Nogueira", "Nowak", "O'Brien", "Obi", "Ogawa", "Okafor", "Okeke", "Omar", "Onishi", "Ortega",
        "Ozturk", "Padilla", "Pahlavi", "Papadopoulos", "Paredes", "Patel", "Pavlovic", "Pereira", "Perez", "Peterson",
        "Pham", "Popescu", "Qureshi", "Rahman", "Rajput", "Ramrez", "Rashid", "Reyes", "Ricci", "Rodríguez",
        "Rogers", "Rossi", "Roth", "Ruiz", "Saito", "Sanchez", "Santos", "Sato", "Schmidt", "Schneider",
        "Schwarz", "Seo", "Shah", "Sharma", "Silva", "Singh", "Smith", "Sokolov", "Souza", "Stefanovic",
        "Subramanian", "Sun", "Suzuki", "Szabo", "Takahashi", "Tamm", "Tan", "Tao", "Teixeira", "Teng",
        "Thakur", "Tran", "Tran", "Tsai", "Turan", "Tzimoulis", "Uddin", "Varela", "Vargas", "Verma",
        "Vidal", "Vo", "Wagner", "Wang", "Wei", "White", "Williams", "Wu", "Xia", "Yamada",
        "Yan", "Yang", "Yilmaz", "Yoshida", "Yu", "Yusuf", "Zhang", "Zhou", "Zhu", "Zivkovic"]
    domains = ["@gmail.com", "@yahoo.com", "@outlook.com","@hotmail.com","@onet.pl" ,"@wp.pl",]

    first_name = random.choice(first_names)
    last_name = random.choice(last_names)
    number = str(random.randint(1, 99)) if random.choice([True, False]) else ''
    email_formats = [
        f"{first_name}.{last_name}{number}",
        f"{first_name}_{last_name}{number}",
        f"{first_name}{number}{last_name}"
    ]
    email = random.choice(email_formats) + random.choice(domains)
    return email

# Helper function to generate random password
def generate_random_password():
    special_chars = "!@#$%^&*"
    password = [
        random.choice(string.ascii_lowercase),
        random.choice(string.ascii_uppercase),
        random.choice(string.digits),
        random.choice(special_chars)
    ]
    password += random.choices(string.ascii_letters + string.digits + special_chars, k=6)
    random.shuffle(password)
    return ''.join(password)

# Helper function to generate random date of birth
def generate_random_dob():
    year = random.randint(1978, 2004)
    month = random.randint(1, 12)
    day = random.randint(1, 28)
    return year, month, day

# Function to handle email if it already exists
def handle_existing_email_error(driver, email_field):
    try:
        # Check if we moved on to the password input (meaning the email was accepted)
        password_input_present = WebDriverWait(driver, 5).until(
            EC.presence_of_element_located((AppiumBy.ID, 'com.spotify.music:id/input_password'))
        )
        if password_input_present:
            print("Email accepted; proceeding to password input.")
            return None  # No new email is generated because the email was accepted
    except TimeoutException:
        print("Password input not found; checking for email error...")

    try:
        # Wait for the "email already connected" popup
        error_popup = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("This email is already connected")'))
        )
        if error_popup.is_displayed():
            print("Email already exists. Generating a new email...")
            dismiss_button = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.spotify.music:id/button_negative")')
            dismiss_button.click()
            time.sleep(2)
            email_field.clear()
            time.sleep(1)
            new_email = generate_random_email()
            email_field.send_keys(new_email)
            print(f"New email generated: {new_email}")
            email_next_button = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/email_next_button')
            time.sleep(1)
            email_next_button.click()
            return new_email  # Return the new email
    except TimeoutException:
        print("No email error popup appeared; proceeding...")

    return None  # Return None if no new email is generated

# Function to select date of birth
def select_random_dob(driver):
    year, month, day = generate_random_dob()
    month_text = time.strftime('%b', time.strptime(str(month), '%m'))
    day_text = f"{day:02d}"

    month_picker = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.NumberPicker").instance(0)')
    month_input = month_picker.find_element(AppiumBy.CLASS_NAME, 'android.widget.EditText')
    month_input.clear()
    month_input.send_keys(month_text)

    day_picker = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.NumberPicker").instance(1)')
    day_input = day_picker.find_element(AppiumBy.CLASS_NAME, 'android.widget.EditText')
    day_input.clear()
    day_input.send_keys(day_text)

    year_picker = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.NumberPicker").instance(2)')
    year_input = year_picker.find_element(AppiumBy.CLASS_NAME, 'android.widget.EditText')
    year_input.clear()
    year_input.send_keys(str(year))
    year_input.send_keys("\n")
    day_picker.click()
    time.sleep(2)

# Function to handle the signup process
def signup_spotify(driver):
    try:
        print("Starting signup process...")

        # Step 1: Click 'Sign up free'
        sign_up_button = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Sign up free")')
        sign_up_button.click()
        print("Clicked 'Sign up free'")

        # Step 2: Generate and enter email, handle existing email case
        email = generate_random_email()
        email_field = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/email')
        email_field.send_keys(email)
        email_next_button = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/email_next_button')
        email_next_button.click()
        handle_existing_email_error(driver, email_field)

        # Check for email already exists error and handle it
        new_email = handle_existing_email_error(driver, email_field)
        if new_email:
            email = new_email  # Update the email if a new one was generated

        # Step 3: Generate password and click 'Next'
        password = generate_random_password()
        password_field = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/input_password')
        password_field.send_keys(password)
        password_next_button = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/password_next_button')
        password_next_button.click()

        # Step 4: Select date of birth
        select_random_dob(driver)
        dob_next_button = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/age_next_button')
        dob_next_button.click()

        # Step 5: Select gender
        gender_button = random.choice(['com.spotify.music:id/gender_button_male', 'com.spotify.music:id/gender_button_female'])
        wait_for_element(driver, AppiumBy.ID, gender_button).click()
        gender_next_button = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/name_next_button')

        # Step 6: Click on 'text required' if visible, then click 'Next'
        try:
            required_text_element = WebDriverWait(driver, 5).until(
                EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.spotify.music:id/text_required")'))
            )
            if required_text_element.is_displayed():
                required_text_element.click()
                print("Clicked on the required text element.")
        except TimeoutException:
            print("'Text required' element not found; skipping...")
        gender_next_button.click()

    # Handle CAPTCHA if present
    
    try:
        recaptcha_text = WebDriverWait(driver, 5).until(
            EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("We need to make sure that you\'re a human")'))
        )
        if recaptcha_text.is_displayed():
            print("Handling reCAPTCHA...")
            recaptcha_anchor = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.TextView").instance(3)')
            recaptcha_anchor.click()
            time.sleep(5)

            recaptcha_help_button = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Solve the challenge")')
            recaptcha_help_button.click()
            time.sleep(10)

            continue_button = wait_for_element(driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Continue")')
            continue_button.click()
            time.sleep(10)
            print("Completed reCAPTCHA.")
    except TimeoutException:
        print("reCAPTCHA not found, proceeding...")


        # Step 8: Check if "Try again later" message appears
        try_again_later = check_try_again_later(driver)
        if try_again_later:
            print("Received 'Try again later' message. Restarting the process...")
            return False

        # Step 9: Save credentials
        save_credentials(email, password)

        # Step 10: Simulate pressing the back button
        press_back_button(driver)

        # Step 11: Check if skip button is visible and click on it
        try:
            skip_button = wait_for_element(driver, AppiumBy.ID, 'com.spotify.music:id/allboarding_skip_dialog_skip_button', timeout=10)
            if skip_button.is_displayed():
                skip_button.click()
                print("Clicked 'Skip' button.")
        except TimeoutException:
            print("'Skip' button not found; skipping...")

        return True

    except Exception as e:
        print(f"An error occurred during account creation: {e}")
        return False

# Function to check for "Try again later" message
def check_try_again_later(driver):
    try:
        try_again_later_element = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Try again later.")'))
        )
        if try_again_later_element.is_displayed():
            return True
    except TimeoutException:
        return False
    return False

# Function to save credentials
def save_credentials(email, password):
    with open("credentials.txt", "a") as f:
        f.write(f"{email}:{password}\n")
    print(f"Credentials saved: {email}:{password}")

# Function to simulate pressing the back button
def press_back_button(driver):
    driver.execute_script('mobile: pressKey', {"keycode": 4})
    print("Pressed the back button.")

# Function to close the app and clear cache using ADB
def close_app_and_clear_cache():
    print("Closing Spotify and clearing cache...")
    subprocess.run(["adb", "shell", "am", "force-stop", "com.spotify.music"])
    subprocess.run(["adb", "shell", "pm", "clear", "com.spotify.music"])
    time.sleep(5)

# Main loop for account creation
for i in range(total_accounts):
    print(f"Starting account creation {i+1}/{total_accounts}...")
    process_completed = False
    driver = None

    while not process_completed:
        try:
            # Initialize Appium session
            driver = webdriver.Remote('http://127.0.0.1:4723', options=options)
            time.sleep(5)
            process_completed = signup_spotify(driver)

            if not process_completed:
                print("Retrying the account creation process...")
                continue

        except Exception as e:
            print(f"An error occurred during account creation: {e}")

        finally:
            if driver:
                press_back_button(driver)
                driver.quit()
            close_app_and_clear_cache()
            print(f"Finished cleanup for account {i+1}/{total_accounts}.\n")