from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
import os
import socket
import sys
import requests
import time
import hashlib
import json

# ===== CONFIGURATION =====
# Update these with your Multilogin credentials and profile information

MULTILOGIN_EMAIL = "e.kamuf88@gmail.com"  # Replace with your Multilogin email
MULTILOGIN_PASSWORD = "Start#12345"  # Replace with your Multilogin password (plain password, will be MD5 hashed)

# Multilogin API endpoints
MLX_SIGNIN_URL = "https://api.multilogin.com/user/signin"
MLX_LAUNCHER_BASE = "https://launcher.mlx.yt:45001/api/v2"
MLX_PROFILE_STOP_URL = "https://launcher.mlx.yt:45001/api/v1/profile/stop"

# Your Multilogin profile configuration
FOLDER_ID = "ec4a6221-f8f3-4843-83d7-d66a3d101c68"  # Replace with your folder ID
PROFILE_ID = "6c40d87c-44e7-4b7b-a7c0-3e985e9d6d75"  # Replace with your profile ID

# Automation settings
AUTOMATION_TYPE = "puppeteer"  # or "selenium"
HEADLESS_MODE = "false"

# Option to stop profile when done (set to False to keep it running)
STOP_PROFILE_WHEN_DONE = False


def sign_in_multilogin(email: str, password: str) -> str:
    """
    Sign in to Multilogin API and get authentication token.
    
    Args:
        email: Multilogin account email
        password: Multilogin account password (will be MD5 hashed)
        
    Returns:
        Authentication token (Bearer token)
    """
    print("=" * 60)
    print("Step 1: Signing in to Multilogin API...")
    print("=" * 60)
    
    # Multilogin API requires MD5 hashed password
    # Check if password is already a hash (32 hex characters)
    if len(password) == 32 and all(c in '0123456789abcdef' for c in password.lower()):
        # Already a hash, use as-is
        password_hash = password
        print("Using provided password hash directly")
    else:
        # Hash the plain password
        password_hash = hashlib.md5(password.encode()).hexdigest()
        print("Hashing provided password")
    
    payload = {
        "email": email,
        "password": password_hash
    }
    
    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json"
    }
    
    try:
        print(f"Signing in with email: {email}")
        response = requests.post(MLX_SIGNIN_URL, json=payload, headers=headers, timeout=30)
        
        # Parse response
        try:
            data = response.json()
        except:
            raise RuntimeError(f"Invalid JSON response: {response.text[:200]}")
        
        # Check response status
        if response.status_code != 200:
            error_msg = data.get("status", {}).get("message", f"HTTP {response.status_code}")
            error_code = data.get("status", {}).get("error_code", "")
            raise ConnectionError(f"Multilogin API returned error {response.status_code}: {error_msg} (code: {error_code})")
        
        # Extract token - check both possible response formats
        if "data" in data and "token" in data["data"]:
            token = data["data"]["token"]
        elif "token" in data:
            token = data["token"]
        else:
            # Show full response for debugging
            print(f"Unexpected response format: {json.dumps(data, indent=2)}")
            raise ValueError("Token not found in response")
        
        if not token:
            raise ValueError("Token is empty in response")
        
        print("✓ Successfully signed in to Multilogin")
        return token
            
    except requests.exceptions.HTTPError as e:
        # Better error message for HTTP errors
        try:
            error_data = response.json()
            error_msg = error_data.get("status", {}).get("message", str(e))
            raise ConnectionError(f"Multilogin API error: {error_msg}")
        except:
            raise ConnectionError(f"Failed to sign in to Multilogin API: {e}")
    except requests.exceptions.RequestException as e:
        raise ConnectionError(f"Failed to sign in to Multilogin API: {e}")
    except (KeyError, ValueError) as e:
        raise RuntimeError(f"Invalid response from Multilogin API: {e}")


def stop_multilogin_profile(profile_id: str, token: str) -> bool:
    """
    Stop a running Multilogin profile.
    
    Args:
        profile_id: The UUID of the profile to stop
        token: Authentication token
        
    Returns:
        True if profile was stopped, False otherwise
    """
    print(f"\nStopping profile {profile_id}...")
    
    url = f"{MLX_PROFILE_STOP_URL}/p/{profile_id}"
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {token}"
    }
    
    try:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        
        data = response.json()
        if data.get("status", {}).get("http_code") == 200:
            print(f"✓ Profile stopped successfully")
            # Wait a moment for profile to fully stop
            time.sleep(2)
            return True
        else:
            print(f"Warning: Profile stop returned: {data}")
            return False
            
    except requests.exceptions.RequestException as e:
        print(f"Warning: Could not stop profile: {e}")
        return False


def start_multilogin_profile(folder_id: str, profile_id: str, token: str, 
                             automation_type: str = "puppeteer", 
                             headless: str = "false") -> int:
    """
    Start a Multilogin profile and get the debugging port.
    
    Args:
        folder_id: The folder ID containing the profile
        profile_id: The UUID of the profile to start
        token: Authentication token
        automation_type: Type of automation ("puppeteer" or "selenium")
        headless: Whether to run in headless mode ("true" or "false")
        
    Returns:
        The remote debugging port number
    """
    print("=" * 60)
    print("Step 2: Starting Multilogin profile...")
    print("=" * 60)
    
    # First, try to stop the profile if it's already running
    stop_multilogin_profile(profile_id, token)
    
    url = f"{MLX_LAUNCHER_BASE}/profile/f/{folder_id}/p/{profile_id}/start"
    params = {
        "automation_type": automation_type,
        "headless_mode": headless
    }
    
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {token}"
    }
    
    try:
        print(f"Starting profile {profile_id}...")
        response = requests.get(url, params=params, headers=headers, timeout=60)
        response.raise_for_status()
        
        data = response.json()
        
        # Check if profile started successfully
        status = data.get("status", {})
        if status.get("http_code") != 200:
            error_msg = status.get("message", "Unknown error")
            
            # If profile is already running, try to stop it and retry
            if "already running" in error_msg.lower() or "already started" in error_msg.lower():
                print("Profile is already running. Stopping it first...")
                if stop_multilogin_profile(profile_id, token):
                    print("Retrying profile start...")
                    time.sleep(3)
                    response = requests.get(url, params=params, headers=headers, timeout=60)
                    response.raise_for_status()
                    data = response.json()
                else:
                    raise RuntimeError("Failed to stop already running profile")
            else:
                raise RuntimeError(f"Profile start failed: {error_msg}")
        
        # Extract port from response
        profile_data = data.get("data", {})
        port_str = profile_data.get("port")
        
        if not port_str:
            raise ValueError("Port not found in profile start response")
        
        port = int(port_str)
        print(f"✓ Profile started successfully on debugging port {port}")
        
        # Wait a moment for browser to be ready
        print("Waiting for browser to be ready...")
        time.sleep(3)
        
        return port
        
    except requests.exceptions.RequestException as e:
        raise ConnectionError(f"Failed to start Multilogin profile: {e}")
    except (KeyError, ValueError) as e:
        raise RuntimeError(f"Invalid response from Multilogin launcher: {e}")


def check_port_accessible(port: int, host: str = "127.0.0.1") -> bool:
    """Check if a port is accessible (browser is listening)."""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(1)
        result = sock.connect_ex((host, port))
        sock.close()
        print("Test")
        return result == 0
    except Exception:
        return False


def handle_cookie_consent(driver: webdriver.Chrome, timeout: int = 10) -> bool:

    print("\nChecking for cookie consent dialog...")
    
    try:
        wait = WebDriverWait(driver, timeout=timeout)
        
        cookie_button_selectors = [
            "//button[contains(text(), 'Allow all cookies')]",
            "//button[contains(text(), 'Allow all')]",
            "//button[@aria-label='Allow all cookies']",
            "//button[contains(@aria-label, 'Allow all')]",
            "//button[@data-testid='cookie-accept-button']",
        ]
        
        for selector in cookie_button_selectors:
            try:
                button = wait.until(EC.element_to_be_clickable((By.XPATH, selector)))
                print(f"Found cookie consent button, clicking 'Allow all cookies'...")
                button.click()
                time.sleep(1)
                print("✓ Cookie consent accepted successfully")
                return True
            except (TimeoutException, NoSuchElementException):
                continue
        
        # Try to find button in cookie dialog containers
        dialog_containers = [
            "//div[contains(@class, 'cookie')]",
            "//div[contains(@id, 'cookie')]",
            "//div[contains(text(), 'Allow the use of cookies')]",
        ]
        
        for container_selector in dialog_containers:
            try:
                container = driver.find_element(By.XPATH, container_selector)
                buttons = container.find_elements(By.TAG_NAME, "button")
                if buttons:
                    print(f"Found cookie dialog container, clicking primary button...")
                    buttons[0].click()
                    time.sleep(1)
                    print("✓ Cookie consent accepted")
                    return True
            except (NoSuchElementException, IndexError):
                continue
        
        print("No cookie consent dialog found (may have already been accepted)")
        return False
        
    except Exception as e:
        print(f"Warning: Could not handle cookie consent: {e}")
        return False


def attach_to_multilogin_browser(port: int) -> webdriver.Chrome:

    print("=" * 60)
    print("Step 3: Attaching Selenium to browser...")
    print("=" * 60)
    
    # Verify the port is accessible
    print(f"Verifying browser is accessible on port {port}...")
    max_retries = 15
    for i in range(max_retries):
        if check_port_accessible(port):
            break
        if i < max_retries - 1:
            print(f"Port {port} not ready yet, waiting... ({i+1}/{max_retries})")
            time.sleep(2)
    else:
        raise ConnectionError(
            f"Browser on port {port} is not accessible after {max_retries} attempts. "
            f"Profile may not have started correctly."
        )
    
    # Configure Chrome options to connect to existing browser
    chrome_options = Options()
    chrome_options.debugger_address = f"127.0.0.1:{port}"
    
    try:
        print(f"Attaching Selenium to browser on port {port}...")
        driver = webdriver.Chrome(options=chrome_options)
        print("✓ Successfully attached to browser!")
        return driver
    except WebDriverException as e:
        if "cannot connect to chrome" in str(e).lower():
            raise ConnectionError(
                f"Failed to attach to browser on port {port}. "
                f"Error: {e}"
            )
        raise


def take_instagram_screenshot(driver: webdriver.Chrome, output_path: str = "instagram_screenshot.png"):

    print("=" * 60)
    print("Step 4: Taking Instagram screenshot...")
    print("=" * 60)
    
    # Navigate to Instagram
    print("\nNavigating to Instagram...")
    driver.get("https://snocks.com/")
    
    # Wait for page to fully load
    print("Waiting for page to load...")
    wait = WebDriverWait(driver, timeout=30)
    try:
        wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
        time.sleep(2)  # Wait for dynamic content
    except TimeoutException:
        print("Warning: Page load timeout, but proceeding...")
    
    # Handle cookie consent dialog automatically
    #handle_cookie_consent(driver, timeout=10)
    
    # Wait a moment after handling cookies
    time.sleep(1)
    
    # Take screenshot and save it
    print(f"\nTaking screenshot...")
    driver.save_screenshot(output_path)
    print(f"✓ Screenshot saved to: {output_path}")


def main():
    
    # Set display environment variable
    os.environ['DISPLAY'] = ':99'
    
    print("\n" + "=" * 60)
    print("Automated Instagram Screenshot Bot")
    print("=" * 60)
    print()
    
    token = None
    driver = None
    
    try:
        # Step 1: Sign in to Multilogin
        token = sign_in_multilogin(MULTILOGIN_EMAIL, MULTILOGIN_PASSWORD)
        
        # Step 2: Start Multilogin profile
        debug_port = start_multilogin_profile(
            FOLDER_ID, 
            PROFILE_ID, 
            token,
            AUTOMATION_TYPE,
            HEADLESS_MODE
        )
        
        # Step 3: Attach Selenium to browser
        driver = attach_to_multilogin_browser(debug_port)
        
        # Step 4: Take Instagram screenshot
        take_instagram_screenshot(driver)
        
        print("\n" + "=" * 60)
        print("✓ All steps completed successfully!")
        print("=" * 60)
        
    except (ConnectionError, RuntimeError, ValueError) as e:
        print(f"\n✗ Error: {e}")
        print("\nTroubleshooting:")
        print("1. Check your Multilogin credentials")
        print("2. Verify FOLDER_ID and PROFILE_ID are correct")
        print("3. Ensure Multilogin agent is running")
        sys.exit(1)
    except Exception as e:
        print(f"\n✗ Unexpected error: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
    finally:
        # Cleanup
        if driver:
            try:
                print("\nDetaching from browser...")
                driver.quit()
            except:
                pass
        
        # Optionally stop the profile
        if STOP_PROFILE_WHEN_DONE and token:
            try:
                print("\nStopping Multilogin profile...")
                stop_multilogin_profile(PROFILE_ID, token)
            except:
                pass


if __name__ == "__main__":
    main()
