use crate::core::chunk;
use crate::core::db::PostgresDb;
use sqlx::PgPool;
use tokio::time::{sleep, Duration};
use tracing;

pub struct JobWorker {
    pool: PgPool,
    poll_interval: Duration,
}

impl JobWorker {
    pub fn new(pool: PgPool, poll_interval_secs: u64) -> Self {
        Self {
            pool,
            poll_interval: Duration::from_secs(poll_interval_secs),
        }
    }

    pub async fn run(&self) {
        tracing::info!(
            "🤖 Job Worker started (Polling every {}s)",
            self.poll_interval.as_secs()
        );

        loop {
            match self.process_next_job().await {
                Ok(has_work) => {
                    if !has_work {
                        // No work found, wait before polling again
                        sleep(self.poll_interval).await;
                    }
                    // If we processed a job, loop immediately to check for more
                }
                Err(e) => {
                    tracing::error!("❌ Job Worker error: {}", e);
                    sleep(Duration::from_secs(5)).await;
                }
            }
        }
    }

    async fn process_next_job(&self) -> anyhow::Result<bool> {
        // 1. Fetch a QUEUED job from monitor_jobs
        // Using sqlx::query_as to map to tuple.
        // Note: progress_total is int4 (i32).
        let job_row: Option<(i32, String, i32)> = sqlx::query_as(
            r#"
            UPDATE dev.monitor_jobs 
            SET status = 'RUNNING', updated_at = NOW()
            WHERE id = (
                SELECT id FROM dev.monitor_jobs 
                WHERE status = 'QUEUED' 
                ORDER BY created_at ASC 
                LIMIT 1 
                FOR UPDATE SKIP LOCKED
            )
            RETURNING id, uuid, COALESCE(progress_total, 0)
            "#,
        )
        .fetch_optional(&self.pool)
        .await?;

        if let Some((job_id, asset_uuid, total_frames)) = job_row {
            tracing::info!(
                "🚀 Processing Job {} for Asset {} (Frames: {})",
                job_id,
                asset_uuid,
                total_frames
            );

            // 2. Execute Logic (Default to rule1 for now as monitor_jobs doesn't store rule type explicitly)
            let fps = self.get_asset_fps(&asset_uuid).await?;
            let db = PostgresDb::from_pool(self.pool.clone());

            let result = chunk::rule1_ingest::execute_rule1(&db, &asset_uuid, fps).await;

            // 3. Update Job Status
            match result {
                Ok(chunk_count) => {
                    tracing::info!(
                        "✅ Job {} completed. Processed {} items.",
                        job_id,
                        chunk_count
                    );

                    // Update monitor_jobs
                    // Using runtime query to avoid compile-time macro checks
                    sqlx::query(
                        "UPDATE dev.monitor_jobs SET status = 'COMPLETED', progress_current = progress_total, updated_at = NOW() WHERE id = $1"
                    )
                    .bind(job_id)
                    .execute(&self.pool)
                    .await?;

                    // Update video processing_status
                    sqlx::query(
                        "UPDATE dev.videos SET processing_status = $1::jsonb WHERE file_uuid = $2",
                    )
                    .bind(serde_json::json!({"status": "COMPLETED"}))
                    .bind(asset_uuid)
                    .execute(&self.pool)
                    .await?;
                }
                Err(e) => {
                    tracing::error!("❌ Job {} failed: {}", job_id, e);
                    let err_msg = e.to_string();
                    let safe_msg = if err_msg.len() > 500 {
                        &err_msg[..500]
                    } else {
                        &err_msg
                    };

                    sqlx::query(
                        "UPDATE dev.monitor_jobs SET status = 'FAILED', last_error = $2, updated_at = NOW() WHERE id = $1"
                    )
                    .bind(job_id)
                    .bind(safe_msg)
                    .execute(&self.pool)
                    .await?;
                }
            }
            return Ok(true); // Processed a job
        }

        Ok(false) // No job found
    }

    async fn get_asset_fps(&self, uuid: &str) -> anyhow::Result<f64> {
        // dev.videos now uses file_uuid and has a direct fps column
        let fps: Option<f64> =
            sqlx::query_scalar("SELECT fps FROM dev.videos WHERE file_uuid = $1")
                .bind(uuid)
                .fetch_optional(&self.pool)
                .await?;

        // Fallback to 29.97 if not found
        Ok(fps.unwrap_or(29.97))
    }
}
