#!/usr/bin/env python3
import sys
import os
import argparse
import logging
import json
import tabulate
import time
import requests
import tqdm

from .lib.api_client import APIClient
from .lib.spark_stage_analyzer import SparkStageAnalyzer
from .lib.s3_uploader import S3Uploader

def setup_logging(verbose=False, debug=False):
    """Set up logging with appropriate verbosity level"""
    if debug:
        level = logging.DEBUG
    elif verbose:
        level = logging.INFO
    else:
        level = logging.WARNING
    
    class TqdmLoggingHandler(logging.Handler):
        def emit(self, record):
            if record.name == 'urllib3.connectionpool' and 'Retrying' in record.getMessage():
                return
            if 'Failed to establish a new connection' in record.getMessage():
                return
            try:
                msg = self.format(record)
                tqdm.tqdm.write(msg)
            except Exception:
                self.handleError(record)
    
    root_logger = logging.getLogger()
    for handler in root_logger.handlers[:]:
        root_logger.removeHandler(handler)
    
    log_format = '%(levelname)s: %(message)s'
    formatter = logging.Formatter(log_format)
    
    handler = TqdmLoggingHandler()
    handler.setFormatter(formatter)
    
    logging.getLogger('urllib3').setLevel(logging.ERROR)
    logging.getLogger('requests').setLevel(logging.ERROR)
    
    root_logger = logging.getLogger()
    root_logger.setLevel(level)
    root_logger.addHandler(handler)
    
    return logging.getLogger(__name__)

def parse_arguments():
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(description='Spark Application Analysis Tool')
    parser.add_argument('--show-readme', action='store_true',
                        help='Display the README documentation')
    parser.add_argument('-l', '--local', action='store_true', 
                        help='Run in local mode using localhost configuration')
    parser.add_argument('-b', '--browser', action='store_true',
                        help='Run in browser mode (when cookies are needed for authentication)')
    parser.add_argument('-s', '--stage_id', type=str, default=None,
                        help='Stage ID to analyze')
    parser.add_argument('-a', '--app_id', type=str, default=None,
                        help='Application ID to analyze')
    parser.add_argument('--opt-out', type=str, default="",
                        help='Comma-separated list of fields to opt out of (name,description,details)')
    parser.add_argument('--upload-url', type=str, default=None,
                        help='URL to upload the JSON output to (will use gzip compression)')
    parser.add_argument('-v', '--verbose', action='store_true',
                        help='Enable verbose logging (INFO level)')
    parser.add_argument('-d', '--debug', action='store_true',
                        help='Enable debug logging (DEBUG level, includes all requests/responses)')
    parser.add_argument('--env', type=str, choices=['staging', 'production'], default='production',
                        help='Environment to use (staging requires SPARK_ANALYZER_STAGING_* env vars, production is default)')
    parser.add_argument('--cost-estimator-id', type=str, default=None,
                        help='Cost estimator user ID (provided when you signed up for the service)')
    parser.add_argument('--server-url', type=str, default=None,
                        help='Custom Spark History Server URL (e.g., http://localhost:18080)')
    return parser.parse_args()

def print_error_box(title, message, help_text=None):
    """Print a visually distinct error message box with proper alignment"""
    message = str(message).replace("│", "|").replace("➤", "->")
    if help_text:
        help_text = str(help_text).replace("│", "|").replace("➤", "->")

    fixed_width = 65
    border_width = fixed_width + 4

    print(f"\n❌ {title} ❌".center(border_width))
    print("┌" + ("─" * (border_width - 2)) + "┐")

    def print_box_line(line):
        print(f"│ {line.ljust(fixed_width)} │")

    def word_wrap(text, width):
        import textwrap
        return textwrap.wrap(text, width=width) or [""]

    for line in word_wrap(message, fixed_width):
        print_box_line(line)

    if help_text:
        print_box_line("")
        print_box_line("How to fix this:")
        print_box_line("")
        help_lines = help_text.split('\n')
        for line in help_lines:
            import re
            number_match = re.match(r'(>\s*\d+\.\s+)(.*)', line)
            if number_match:
                prefix = number_match.group(1)
                content = number_match.group(2)
                indent = len(prefix)
                wrap_width = fixed_width - indent
                wrapped = word_wrap(content, wrap_width)
                if wrapped:
                    print_box_line(f"{prefix}{wrapped[0]}")
                    for cont_line in wrapped[1:]:
                        print_box_line(" " * indent + cont_line)
                else:
                    print_box_line(prefix)
            else:
                for wrapped_line in word_wrap(line, fixed_width):
                    print_box_line(wrapped_line)
    print("└" + ("─" * (border_width - 2)) + "┘")

def upload_output(data: dict, upload_url: str) -> bool:
    """Upload JSON data to the specified URL using gzip compression."""
    try:
        json_data = json.dumps(data)
        headers = {
            'Content-Type': 'application/json',
            'Content-Encoding': 'gzip',
            'Accept-Encoding': 'gzip'
        }
        
        logging.debug(f"Uploading data to {upload_url} (size: {len(json_data)} bytes)")
        
        response = requests.post(
            upload_url,
            data=json_data,
            headers=headers,
            stream=True
        )
        
        response.raise_for_status()
        logging.info(f"Successfully uploaded to {upload_url}")
        return True
        
    except requests.exceptions.ConnectionError as e:
        print_error_box(
            "CONNECTION ERROR", 
            f"Could not connect to {upload_url}",
            f"Check your internet connection\nVerify the URL is correct and accessible"
        )
        logging.debug(f"Connection error details: {str(e)}")
        return False
    except requests.exceptions.HTTPError as e:
        status_code = e.response.status_code
        print_error_box(
            f"HTTP ERROR {status_code}", 
            f"Server returned error: {e.response.text[:200]}",
            f"Check if the URL is correct\nVerify you have permission to upload to this endpoint"
        )
        logging.debug(f"HTTP error details: {str(e)}")
        return False
    except Exception as e:
        print_error_box(
            "UPLOAD ERROR", 
            f"Error uploading data: {str(e)}",
            "Try again later or use a different upload method"
        )
        logging.debug(f"Upload error details: {str(e)}")
        return False

def get_user_input(prompt, validator=None):
    """Get user input with proper interrupt handling"""
    try:
        while True:
            try:
                user_input = input(prompt).strip()
                if validator and not validator(user_input):
                    continue
                return user_input
            except KeyboardInterrupt:
                print("\n🛑 Analysis interrupted by user.")
                sys.exit(0)
    except KeyboardInterrupt:
        print("\n🛑 Analysis interrupted by user.")
        sys.exit(0)

def process_app_id(app_id, applications, client, spark_stage_analyzer, s3_uploader, args, s3_upload_enabled, processed_app_ids, successful_upload_app_ids, failed_upload_app_ids, summary_app_ids, app_index=0, app_count=1):
    """Process a single application ID and return the result status."""
    print(f"\n🔄 Processing application {app_id} ({app_index + 1}/{app_count})...")
    application_name = next((app['name'] for app in applications if app['id'] == app_id), None)
    if application_name:
        print(f"📝 Application name: {application_name}")
    try:
        print("🔄 Fetching stages...")
        stages = client.get_stages(app_id)
        print(f"📋 Found {len(stages)} stages")
        if len(stages) == 0:
            print_error_box(
                "NO STAGES FOUND",
                f"Application {app_id} has no stages to analyze.",
                "This could be because:\n" +
                "1. The application is still running\n" +
                "2. The application hasn't started any stages yet\n" +
                "3. The application has completed but no stages were recorded\n\n" +
                "Please try analyzing a different application."
            )
            return 'zero_stages'
        if app_id not in summary_app_ids:
            summary_app_ids.append(app_id)
    except Exception as e:
        print_error_box(
            "STAGE FETCH ERROR",
            f"Failed to fetch stages for application {app_id}",
            "This application might be incomplete, still running, or there was a connection issue.\nTry analyzing a different application or check your network/server."
        )
        logging.debug(f"Stage fetch error details: {str(e)}")
        return 'connection_error'

    print("🔄 Fetching executor metrics...")
    try:
        executor_metrics = client.get_all_executor_metrics(app_id)
        print(f"📋 Found {len(executor_metrics)} executors (including historical)")
    except Exception as e:
        print_error_box(
            "EXECUTOR METRICS ERROR",
            f"Failed to fetch executor metrics for application {app_id}",
            "Proceeding with limited executor information\nSome executor details may be missing in the analysis"
        )
        logging.debug(f"Executor metrics error details: {str(e)}")
        executor_metrics = []

    total_executors = len([e for e in executor_metrics if e["id"] != "driver"])
    total_cores = sum(e.get("totalCores", 0) for e in executor_metrics if e["id"] != "driver")
    presigned_urls = None
    batch_filenames = []
    proceed_with_processing = True

    if s3_upload_enabled and len(stages) > 0:
        batch_size = 1000
        total_batches = (len(stages) + batch_size - 1) // batch_size
        print(f"🔄 Preparing {total_batches} batches for S3 upload (up to {batch_size} stages each)...")
        for batch_idx in range(total_batches):
            start_idx = batch_idx * batch_size
            end_idx = min(start_idx + batch_size, len(stages))
            batch_range = f"{start_idx}-{end_idx-1}"
            batch_filename = f"stages_{app_id}_batch{batch_range}.json"
            batch_filenames.append(batch_filename)

        print(f"🔄 Requesting pre-signed URLs for {len(batch_filenames)} batches upfront...")
        try:
            presigned_urls = s3_uploader.get_presigned_urls(app_id, batch_filenames)
        except Exception as e:
            presigned_urls = None
            if args.debug:
                logging.debug(f"Pre-signed URL error details: {str(e)}")

        if not presigned_urls or len(presigned_urls) != len(batch_filenames):
            print_error_box(
                "UPLOAD PREPARATION ERROR",
                f"Unable to prepare analysis upload for application {app_id}",
                "Verify your cost estimator user ID is correct\nContact Onehouse support if the problem persists\nRun with --debug for more details"
            )
            if args.debug:
                logging.error(f"Failed to get pre-signed URLs: got {len(presigned_urls) if presigned_urls else 0}, expected {len(batch_filenames)}")
            print(f"The analysis could not be uploaded for application {app_id}. Please re-run the tool or contact support if the issue persists.")
            proceed_with_processing = False
            failed_upload_app_ids.append(app_id)
        else:
            successful_upload_app_ids.append(app_id)

    if not proceed_with_processing:
        return 'upload_error'

    print("🔄 Analyzing stages and generating output...")
    proto_output = {
        "application_id": app_id,
        "total_executors": total_executors,
        "total_cores": total_cores,
        "stages": [],
    }

    failed_stages = 0
    for stage in tqdm.tqdm(stages, desc="Processing stages", unit="stage", dynamic_ncols=True, leave=True):
        try:
            stage_data = spark_stage_analyzer.format_stage_for_proto(stage, app_id, executor_metrics)
            if stage_data:
                proto_output["stages"].append(stage_data)
        except Exception as e:
            failed_stages += 1
            logging.debug(f"Error processing stage {stage.get('stageId')}: {str(e)}")

    if failed_stages > 0:
        print(f"⚠️  {failed_stages} stages encountered errors and were skipped")
        logging.info(f"Failed stages for application {app_id}: {failed_stages} out of {len(stages)}")

    if s3_upload_enabled and presigned_urls and len(presigned_urls) == len(batch_filenames):
        for presigned_url in presigned_urls:
            json_data = json.dumps(proto_output)
            success = s3_uploader.upload_json_data(presigned_url, json_data)
            if not success:
                logging.error(f"Failed to upload {batch_filename} to S3")

    if s3_upload_enabled and presigned_urls and len(presigned_urls) == len(batch_filenames):
        success = s3_uploader.signal_application_completion(app_id, batch_filenames)
        if not success:
            print(f"\n❌ Error: Failed to signal completion for application {app_id}. Upload may be incomplete.")
            logging.error(f"Failed to signal completion for application {app_id}")
        else:
            print(f"\n✅ Signaled completion for application {app_id}!")

    print(f"\n✅ Stage analysis complete for {app_id}!")
    processed_app_ids.append(app_id)
    return 'success'

def run_analysis():
    """Main analysis function that orchestrates the entire process."""
    args = parse_arguments()
    logger = setup_logging(args.verbose, args.debug)
    
    try:
        print("🚀 Spark History Server Analyzer")
        print("===============================")
        
        print("⚙️  Initializing components...")
        
        if args.server_url:
            client = APIClient(base_url=args.server_url, args=args)
            print(f"ℹ️  Using custom server URL: {args.server_url}")
        else:
            client = APIClient(args=args)
        
        spark_stage_analyzer = SparkStageAnalyzer(client)
        
        use_staging = args.env == 'staging'
        s3_uploader = S3Uploader(config_file="config.ini", use_staging=use_staging, args=args)
        s3_upload_enabled = s3_uploader.is_upload_enabled()
        
        if not s3_upload_enabled:
            current_env = "staging" if s3_uploader.is_staging_mode() else "production"
            staging_requested = s3_uploader.was_staging_explicitly_requested()
            
            if staging_requested:
                print_error_box(
                    "CONFIGURATION ERROR",
                    "Environment requested but not properly configured.",
                    "Make sure all required environment variables are set:\n" +
                    "- SPARK_ANALYZER_STAGING_API_URL\n" +
                    "- SPARK_ANALYZER_STAGING_API_KEY\n" + 
                    "- SPARK_ANALYZER_STAGING_API_SECRET\n" + 
                    "- SPARK_ANALYZER_STAGING_ORG_ID\n" + 
                    "- SPARK_ANALYZER_STAGING_USER_ID\n" + 
                    "- SPARK_ANALYZER_STAGING_COST_ESTIMATOR_USER_ID"
                )
            else:
                config_path = s3_uploader.get_config_file_path() or "~/.spark_analyzer/configs/config.ini"
                print_error_box(
                    "CONFIGURATION ERROR",
                    "Cost estimator user ID not configured.",
                    f"Add the following to {config_path}:\n\n" +
                    "[cost_estimator]\n" +
                    "user_id = YOUR_USER_ID_HERE\n\n" +
                    "Or run with the --cost-estimator-id option:\n" +
                    "spark-analyzer --cost-estimator-id YOUR_USER_ID_HERE\n\n" +
                    "You should have received this ID when you signed up"
                )
            sys.exit(1)

        opt_out_fields = set()
        if args.opt_out:
            opt_out_fields = set(args.opt_out.split(","))
            spark_stage_analyzer.set_opt_out_fields(opt_out_fields)
            logging.info(f"Using opt-out fields: {opt_out_fields}")

        app_ids = []
        if args.app_id:
            app_ids = [args.app_id]
            logging.info(f"Using provided application ID: {args.app_id}")
        else:
            try:
                print("🔄 Fetching available applications...")
                max_app_fetch_retries = 1
                fetch_attempt = 0
                applications = None
                
                print("⏳ Connecting to Spark History Server...")
                
                while fetch_attempt < max_app_fetch_retries:
                    try:
                        applications = client.get_applications()
                        break
                    except requests.exceptions.ConnectionError as e:
                        fetch_attempt += 1
                        if fetch_attempt < max_app_fetch_retries:
                            retry_delay = 2 ** fetch_attempt
                            logging.debug(f"Connection error - retrying in {retry_delay}s (attempt {fetch_attempt}/{max_app_fetch_retries})")
                            if fetch_attempt == 1:
                                print("⚠️  Connection to server failed, retrying...")
                            time.sleep(retry_delay)
                        else:
                            raise
                    except requests.exceptions.Timeout as e:
                        fetch_attempt += 1
                        if fetch_attempt < max_app_fetch_retries:
                            retry_delay = 2 ** fetch_attempt
                            logging.debug(f"Timeout error - retrying in {retry_delay}s (attempt {fetch_attempt}/{max_app_fetch_retries})")
                            if fetch_attempt == 1:
                                print("⚠️  Server response timed out, retrying...")
                            time.sleep(retry_delay)
                        else:
                            raise

                if not applications:
                    print_error_box(
                        "NO APPLICATIONS FOUND",
                        "No applications were found in the Spark History Server",
                        "Check if the Spark History Server has any applications\n"
                        "Verify your connection settings\n"
                        "Make sure Spark History Server is running and accessible\n"
                        "If using browser mode, verify your cookies are valid"
                    )
                    sys.exit(1)

                if len(applications) == 0:
                    print("⚠️  The Spark History Server contains no applications.")
                    print("   You need to specify an application ID manually.")
                    while True:
                        app_to_analyze = get_user_input("\n🔍 Enter the application id(s) to analyze, separated by commas without spaces (required): ")
                        if not app_to_analyze:
                            print("\n❌ Error: Application ID is required.")
                            print("Please enter at least one application ID to proceed.")
                            continue
                        app_ids = [aid.strip() for aid in app_to_analyze.split(',') if aid.strip()]
                        if not app_ids:
                            print("\n❌ Error: No valid application IDs provided.")
                            print("Please enter at least one application ID to proceed.")
                            continue
                        break
                else:
                    print(f"📋 Found {len(applications)} applications")
                    valid_app_ids = {app['id'] for app in applications}
                    
                    app_table = []
                    for app in applications:
                        app_table.append([app.get('id', 'N/A'), app.get('name', 'N/A')])
                    
                    headers = ["Application ID", "Application Name"]
                    print("\nAvailable Applications:")
                    print(tabulate.tabulate(app_table, headers=headers, tablefmt="grid"))
                    
                    while True:
                        app_to_analyze = get_user_input("\n🔍 Enter the application id(s) to analyze, separated by commas without spaces (required): ")
                        if not app_to_analyze:
                            print("\n❌ Error: Application ID is required.")
                            print("Please enter at least one application ID from the list above.")
                            continue
                        
                        entered_app_ids = [aid.strip() for aid in app_to_analyze.split(',') if aid.strip()]
                        if not entered_app_ids:
                            print("\n❌ Error: No valid application IDs provided.")
                            print("Please enter at least one application ID from the list above.")
                            continue
                        
                        invalid_ids = [aid for aid in entered_app_ids if aid not in valid_app_ids]
                        if invalid_ids:
                            print(f"\n❌ Invalid application ID(s): {', '.join(invalid_ids)}")
                            print("Please enter only valid application IDs from the list above.")
                            continue
                        
                        app_ids = entered_app_ids
                        break

            except requests.exceptions.ConnectionError as e:
                print_error_box(
                    "CONNECTION ERROR",
                    "Failed to connect to the Spark History Server",
                    "Check that the server is running and accessible\n"
                    "Verify your network connection and any VPN settings\n"
                    "Confirm the server URL in your configuration file\n"
                    f"Current URL: {client.base_url}\n"
                    "If you know the application ID, you can specify it directly using --app_id"
                )
                
                while True:
                    proceed = get_user_input("\nDo you want to proceed by manually entering an application ID? (y/n): ")
                    if proceed.lower().strip() not in ['y', 'yes', 'n', 'no']:
                        print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")
                        continue
                    if proceed.lower().strip() in ['n', 'no']:
                        sys.exit(1)
                    
                    app_to_analyze = get_user_input("\n🔍 Enter the application id(s) to analyze, separated by commas without spaces (required): ")
                    if not app_to_analyze:
                        print("\n❌ Error: Application ID is required.")
                        print("Please enter at least one application ID to proceed.")
                        continue
                    app_ids = [aid.strip() for aid in app_to_analyze.split(',') if aid.strip()]
                    if not app_ids:
                        print("\n❌ Error: No valid application IDs provided.")
                        print("Please enter at least one application ID to proceed.")
                        continue
                    break

            except requests.exceptions.Timeout as e:
                print_error_box(
                    "CONNECTION TIMEOUT",
                    "The request to the Spark History Server timed out",
                    "The server might be slow or processing a large amount of data\n"
                    "Try again later or with a longer timeout setting\n"
                    "Consider using port forwarding with SSH if connecting to a remote server\n"
                    "If you know the application ID, you can specify it directly using --app_id"
                )
                
                while True:
                    proceed = get_user_input("\nDo you want to proceed by manually entering an application ID? (y/n): ")
                    if proceed.lower().strip() not in ['y', 'yes', 'n', 'no']:
                        print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")
                        continue
                    if proceed.lower().strip() in ['n', 'no']:
                        sys.exit(1)
                    
                    app_to_analyze = get_user_input("\n🔍 Enter the application id(s) to analyze, separated by commas without spaces (required): ")
                    if not app_to_analyze:
                        print("\n❌ Error: Application ID is required.")
                        print("Please enter at least one application ID to proceed.")
                        continue
                    app_ids = [aid.strip() for aid in app_to_analyze.split(',') if aid.strip()]
                    if not app_ids:
                        print("\n❌ Error: No valid application IDs provided.")
                        print("Please enter at least one application ID to proceed.")
                        continue
                    break

            except Exception as e:
                print_error_box(
                    "APPLICATION FETCH ERROR",
                    f"Failed to fetch Spark applications: {str(e)}",
                    "Check your connection settings and History Server URL\n"
                    "Verify the server is running and accessible\n"
                    "Run with --debug flag for more detailed error information\n"
                    "If you know the application ID, you can specify it directly using --app_id"
                )
                
                while True:
                    proceed = get_user_input("\nDo you want to proceed by manually entering an application ID? (y/n): ")
                    if proceed.lower().strip() not in ['y', 'yes', 'n', 'no']:
                        print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")
                        continue
                    if proceed.lower().strip() in ['n', 'no']:
                        logging.debug(f"Application fetch error details: {str(e)}")
                        sys.exit(1)
                    
                    app_to_analyze = get_user_input("\n🔍 Enter the application id(s) to analyze, separated by commas without spaces (required): ")
                    if not app_to_analyze:
                        print("\n❌ Error: Application ID is required.")
                        print("Please enter at least one application ID to proceed.")
                        continue
                    app_ids = [aid.strip() for aid in app_to_analyze.split(',') if aid.strip()]
                    if not app_ids:
                        print("\n❌ Error: No valid application IDs provided.")
                        print("Please enter at least one application ID to proceed.")
                        continue
                    break

        if app_ids and len(app_ids) > 0:
            skipped_zero_stage_apps = []
            processed_app_ids = []
            summary_app_ids = []
            successful_upload_app_ids = []
            failed_upload_app_ids = []

            for app_index, app_id in enumerate(app_ids):
                while True:
                    result = process_app_id(app_id, applications, client, spark_stage_analyzer, s3_uploader, args, s3_upload_enabled, processed_app_ids, successful_upload_app_ids, failed_upload_app_ids, summary_app_ids, app_index, len(app_ids))
                    if result == 'success':
                        break
                    if result == 'zero_stages':
                        while True:
                            retry = get_user_input("\nWould you like to try another application ID? (y/n): ")
                            if retry.lower().strip() in ['y', 'yes']:
                                break
                            elif retry.lower().strip() in ['n', 'no']:
                                sys.exit(0)
                            else:
                                print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")
                        while True:
                            new_app_id = get_user_input("\n🔍 Enter another application ID to analyze (required): ")
                            if not new_app_id.strip():
                                print("\n❌ Error: Application ID is required.")
                                continue
                            if not any(app['id'] == new_app_id.strip() for app in applications):
                                print(f"\n❌ Error: Application {new_app_id} not found in the list of available applications.")
                                continue
                            app_id = new_app_id.strip()
                            new_result = process_app_id(new_app_id.strip(), applications, client, spark_stage_analyzer, s3_uploader, args, s3_upload_enabled, processed_app_ids, successful_upload_app_ids, failed_upload_app_ids, summary_app_ids, app_index, len(app_ids))
                            if new_result == 'zero_stages':
                                continue
                            break
                        if new_result == 'success' or new_result == 'upload_error':
                            break
                        continue
                    elif result == 'connection_error':
                        while True:
                            retry = get_user_input("\nConnection error occurred. Would you like to try another application ID? (y/n): ")
                            if retry.lower().strip() in ['y', 'yes']:
                                break
                            elif retry.lower().strip() in ['n', 'no']:
                                sys.exit(0)
                            else:
                                print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")
                        while True:
                            new_app_id = get_user_input("\n🔍 Enter another application ID to analyze (required): ")
                            if not new_app_id.strip():
                                print("\n❌ Error: Application ID is required.")
                                continue
                            if not any(app['id'] == new_app_id.strip() for app in applications):
                                print(f"\n❌ Error: Application {new_app_id} not found in the list of available applications.")
                                continue
                            app_id = new_app_id.strip()
                            break
                        continue
                    else:
                        break

            if skipped_zero_stage_apps:
                print("\nThe following application IDs had 0 stages and were skipped:")
                for skipped_id in skipped_zero_stage_apps:
                    print(f"  - {skipped_id}")
                print("")
                while True:
                    overall_retry = get_user_input("Would you like to re-enter a different application ID for any of the skipped apps? (y/n): ")
                    if overall_retry.lower().strip() in ['n', 'no']:
                        break
                    elif overall_retry.lower().strip() in ['y', 'yes']:
                        for skipped_id in skipped_zero_stage_apps:
                            while True:
                                retry = get_user_input(f"Would you like to re-enter a different application ID for {skipped_id}? (y/n): ")
                                if retry.lower().strip() in ['y', 'yes']:
                                    while True:
                                        new_app_id = get_user_input("\n🔍 Enter a replacement application ID to analyze (required): ")
                                        if not new_app_id.strip():
                                            print("\n❌ Error: Application ID is required.")
                                            continue
                                        if not any(app['id'] == new_app_id.strip() for app in applications):
                                            print(f"\n❌ Error: Application {new_app_id} not found in the list of available applications.")
                                            continue
                                        if new_app_id.strip() in processed_app_ids:
                                            print(f"\n❌ Error: Application {new_app_id} has already been processed.")
                                            continue
                                        print(f"\n🔄 Processing replacement application {new_app_id.strip()}...")
                                        result = process_app_id(new_app_id.strip(), applications, client, spark_stage_analyzer, s3_uploader, args, s3_upload_enabled, processed_app_ids, successful_upload_app_ids, failed_upload_app_ids, summary_app_ids, 0, 1)
                                        if result == 'zero_stages' or result == 'connection_error':
                                            continue
                                        break
                                    break
                                elif retry.lower().strip() in ['n', 'no']:
                                    break
                                else:
                                    print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")
                        break
                    else:
                        print("\n❌ Error: Please enter 'y' for yes or 'n' for no.")

            if s3_upload_enabled and successful_upload_app_ids:
                print("🔄 Finalizing upload process...")
                try:
                    if s3_uploader.signal_all_jobs_completion(successful_upload_app_ids):
                        print("✅ Upload finalized successfully")
                        print("\n📬 Onehouse has received and is now processing your Spark job metadata.")
                        print("📊 You will receive your custom report of Spark optimization opportunities shortly.")
                    else:
                        print("ℹ️  Upload partially complete - your data was saved but processing may be delayed")
                except Exception as e:
                    print("ℹ️  Upload partially complete - your data was saved but processing may be delayed")
                    if args.debug:
                        logging.debug(f"Completion signal error details: {str(e)}")
            
            print("\n" + "─" * 50)
            print("📋 PROCESS SUMMARY")
            print("─" * 50)
            
            print("✅ Analysis: Complete - Successfully analyzed the following Spark application(s):")
            for app_id in summary_app_ids:
                print(f"   - {app_id}")
            print("")
            if s3_upload_enabled or args.upload_url:
                if successful_upload_app_ids:
                    print(f"✅ Upload: Data was successfully uploaded for {len(successful_upload_app_ids)} application(s):")
                    for app_id in successful_upload_app_ids:
                        print(f"   - {app_id}")
                    print("")
                if failed_upload_app_ids:
                    print(f"❌ Upload: Data could NOT be uploaded for {len(failed_upload_app_ids)} application(s):")
                    for app_id in failed_upload_app_ids:
                        print(f"   - {app_id} (see error above)")
                    print("")
                if not failed_upload_app_ids:
                    print("✅ All uploads completed successfully.")
            else:
                print("ℹ️ Upload: Not configured - No upload destinations were specified")
            print("─" * 50)
        else:
            print("\n❌ No applications selected for analysis. Exiting.")
            
    except KeyboardInterrupt:
        print("\n🛑 Analysis interrupted by user.")
        sys.exit(0)
    except Exception as e:
        print_error_box(
            "UNEXPECTED ERROR",
            f"An unexpected error occurred: {str(e)}",
            "Try running with --debug flag for more detailed information\nCheck your configuration and try again"
        )
        if args.debug:
            import traceback
            traceback.print_exc()
        logging.debug(f"Unexpected error details: {str(e)}")
        sys.exit(1)

def show_readme():
    """Display the README documentation."""
    try:
        import pkg_resources
        readme_content = pkg_resources.resource_string('spark_analyzer', 'README.md').decode('utf-8')
        print(readme_content)
    except Exception as e:
        print_error_box(
            "README ERROR",
            f"Failed to display README: {str(e)}",
            "The README file could not be found or read.\n"
            "You can view the documentation online at:\n"
            "https://pypi.org/project/spark-analyzer/"
        )
        sys.exit(1)

def main():
    args = parse_arguments()
    if args.show_readme:
        show_readme()
        sys.exit(0)
    run_analysis()

if __name__ == "__main__":
    main() 