/// Watch tuning progress with live updates (poll every 5 seconds)
async fn watch_tuning_progress(
    api_url: &str,
    jwt_token: &str,
    job_id: &str,
) -> AnyhowResult<()> {
    use tokio::time::{sleep, Duration};

    let mut last_trial: u32 = 0;
    let mut iteration: u32 = 0;

    // Validate job ID format once at the start
    let job_id_uuid = Uuid::parse_str(job_id)
        .context("❌ Invalid job ID format (expected UUID)")?;

    // Create gRPC client once (reuse connection)
    let mut client = MlTrainingServiceClient::connect(api_url.to_string())
        .await
        .context("Failed to connect to API")?;

    loop {
        iteration += 1;

        // Create gRPC request with JWT metadata
        let mut request = tonic::Request::new(GetTuningJobStatusRequest {
            job_id: job_id_uuid.to_string(),
        });

        request.metadata_mut().insert(
            "authorization",
            format!("Bearer {}", jwt_token)
                .parse()
                .context("Failed to parse JWT token")?
        );

        // Execute gRPC call to get live status
        let response = client
            .get_tuning_job_status(request)
            .await
            .context("Failed to get tuning job status")?;

        let status_response = response.into_inner();

        // Calculate progress percentage
        let progress_percent = if status_response.total_trials > 0 {
            (status_response.current_trial as f32 / status_response.total_trials as f32) * 100.0
        } else {
            0.0
        };

        // Calculate elapsed time
        let elapsed_seconds = if status_response.started_at > 0 {
            chrono::Utc::now().timestamp() - status_response.started_at
        } else {
            0
        };

        // Extract best Sharpe ratio from metrics
        let best_sharpe_ratio = status_response
            .best_metrics
            .get("sharpe_ratio")
            .copied()
            .unwrap_or(0.0);

        // Format status string
        let status_str = format_tuning_status(status_response.status());

        // Clear previous output (move cursor up and clear lines)
        if iteration > 1 {
            // Clear the previous display (9 lines)
            print!("\x1B[9A\x1B[J");
        }

        // Display rich progress UI
        println!("┌─────────────────────────────────────────────────────────┐");
        println!("│ {} Tuning Job: {}             │", "🎯".bright_cyan(), job_id.chars().take(8).collect::<String>());
        println!("├─────────────────────────────────────────────────────────┤");
        println!("│ Trials: {}/{} ({:.1}%)                                 │",
            status_response.current_trial,
            status_response.total_trials,
            progress_percent
        );
        println!("│ {} Best Sharpe Ratio: {}                         │",
            "🏆".bright_yellow(),
            format!("{:.4}", best_sharpe_ratio).bright_green()
        );

        // Show trial progress indicator if trial changed
        if status_response.current_trial > last_trial {
            println!("│ {} Current Trial #{}: Running...                    │",
                "🔄".bright_blue(),
                status_response.current_trial
            );
            last_trial = status_response.current_trial;
        } else {
            println!("│ {} Status: {}                                  │",
                "📊".bright_white(),
                format_status_colored(&status_str)
            );
        }

        // Progress bar
        let progress_bar = create_progress_bar(progress_percent);
        println!("│ {}     │", progress_bar);

        // Elapsed time
        let elapsed_minutes = elapsed_seconds / 60;
        let elapsed_seconds_remainder = elapsed_seconds % 60;
        println!("│ ⏱️  Elapsed: {}m {}s                                     │",
            elapsed_minutes, elapsed_seconds_remainder
        );
        println!("└─────────────────────────────────────────────────────────┘");

        // Check if job is complete
        match status_response.status() {
            TuningJobStatus::TuningCompleted => {
                println!("\n✅ Tuning job completed successfully!");
                println!("   Best Sharpe Ratio: {}", format!("{:.4}", best_sharpe_ratio).bright_green());
                println!("\n💡 Get best parameters with:");
                println!("   tli tune best --job-id {}", job_id);
                break;
            }
            TuningJobStatus::TuningFailed => {
                println!("\n❌ Tuning job failed!");
                if !status_response.message.is_empty() {
                    println!("   Error: {}", status_response.message);
                }
                break;
            }
            TuningJobStatus::TuningStopped => {
                println!("\n🛑 Tuning job stopped by user");
                println!("\n💡 Get partial results with:");
                println!("   tli tune best --job-id {}", job_id);
                break;
            }
            _ => {
                // Still running, continue polling
            }
        }

        // Wait 5 seconds before next poll
        sleep(Duration::from_secs(5)).await;
    }

    Ok(())
}

/// Format tuning job status as string
fn format_tuning_status(status: TuningJobStatus) -> String {
    match status {
        TuningJobStatus::TuningUnknown => "UNKNOWN",
        TuningJobStatus::TuningPending => "PENDING",
        TuningJobStatus::TuningRunning => "RUNNING",
        TuningJobStatus::TuningCompleted => "COMPLETED",
        TuningJobStatus::TuningFailed => "FAILED",
        TuningJobStatus::TuningStopped => "STOPPED",
    }.to_string()
}
