use crate::config::ExecutorConfig;
use crate::run_config::RunConfig;
use crate::runner::{execute_backtest, execute_run};
use sqlx::PgPool;
use std::sync::Arc;
use std::time::Duration;
use swym_dal::models::strategy_config::StrategyConfig;
use swym_dal::repo::paper_run;
use tokio::sync::Semaphore;
use tracing::{debug, error, info};

pub async fn run_poll_loop(pool: PgPool, config: &ExecutorConfig) {
    let worker_id = config
        .worker_id
        .clone()
        .unwrap_or_else(|| gethostname().unwrap_or_else(|| "unknown".to_owned()));

    let semaphore = Arc::new(Semaphore::new(config.max_concurrent_runs));
    let poll_interval = Duration::from_secs(config.poll_interval_secs);

    info!(
        %worker_id,
        max_concurrent = config.max_concurrent_runs,
        poll_secs = config.poll_interval_secs,
        "starting executor poll loop"
    );

    loop {
        tokio::select! {
            _ = tokio::time::sleep(poll_interval) => {
                // Try to acquire a semaphore permit
                let permit = match semaphore.clone().try_acquire_owned() {
                    Ok(p) => p,
                    Err(_) => {
                        debug!("all run slots full, skipping poll cycle");
                        continue;
                    }
                };

                // Try to claim a queued run
                match paper_run::claim_next_queued(&pool, &worker_id).await {
                    Ok(Some(run)) => {
                        let run_id = run.id;
                        let pool = pool.clone();
                        info!(%run_id, "claimed paper run");

                        tokio::spawn(async move {
                            let _permit = permit; // Hold permit until task completes

                            // Deserialize RunConfig from the run's config
                            let run_config: RunConfig = match serde_json::from_value(run.config.clone()) {
                                Ok(c) => c,
                                Err(e) => {
                                    error!(%run_id, ?e, "invalid run config in paper run");
                                    let _ = paper_run::mark_failed(&pool, run_id, &format!("invalid config: {e}")).await;
                                    return;
                                }
                            };

                            let starts_at = run.starts_at;
                            let finishes_at = run.finishes_at;
                            let risk_free_return = run.risk_free_return;
                            let mode = run.mode.clone();

                            // For rule-based strategies the candle interval is embedded in the
                            // strategy config itself; fall back to the paper run row for compiled
                            // strategies that were created with an explicit candle_interval.
                            let candle_interval = match &run_config.strategy {
                                StrategyConfig::RuleBased(params) => {
                                    Some(params.candle_interval.clone())
                                }
                                _ => run.candle_interval.clone(),
                            };

                            info!(%run_id, %mode, ?starts_at, ?finishes_at, ?candle_interval, "executing paper run");

                            let result = if mode == "backtest" {
                                execute_backtest(&pool, run_id, run_config, starts_at, finishes_at, risk_free_return, candle_interval.as_deref()).await
                            } else {
                                execute_run(run_config, starts_at, finishes_at, risk_free_return).await
                            };

                            match result {
                                Ok(summary) => {
                                    info!(%run_id, "paper run completed successfully");
                                    if let Err(e) = paper_run::mark_complete(&pool, run_id, &summary).await {
                                        error!(%run_id, ?e, "failed to mark run as complete");
                                    }
                                }
                                Err(e) => {
                                    error!(%run_id, ?e, "paper run failed");
                                    let _ = paper_run::mark_failed(&pool, run_id, &e.to_string()).await;
                                }
                            }
                        });
                    }
                    Ok(None) => {
                        // No queued runs, release permit and wait
                        drop(permit);
                    }
                    Err(e) => {
                        error!(?e, "failed to poll for queued runs");
                        drop(permit);
                    }
                }
            }
            _ = tokio::signal::ctrl_c() => {
                info!("shutdown signal received, stopping poll loop");
                break;
            }
        }
    }

    // Wait for in-flight runs to finish
    info!("waiting for in-flight runs to complete...");
    let _ = semaphore
        .acquire_many(config.max_concurrent_runs as u32)
        .await;
    info!("all runs complete, executor shutting down");
}

fn gethostname() -> Option<String> {
    std::fs::read_to_string("/etc/hostname")
        .ok()
        .map(|s| s.trim().to_owned())
}
