//! Callbacks de fin de traitement pour le Stream Processing Thread
//!
//! Ce module définit les callbacks appelés à la fin du traitement d'un job:
//! - on_success: Job terminé avec succès
//! - on_error: Job échoué avec une erreur
//! - on_timeout: Job expiré (timeout)

use crate::error::AppError;
use sqlx::PgPool;
use std::path::PathBuf;
use uuid::Uuid;

/// Type de fin de traitement
#[derive(Debug, Clone)]
pub enum ProcessingEnd {
    /// Traitement terminé normalement
    Normal,
    /// Erreur avec message
    Error(String),
    /// Timeout atteint
    Timeout,
}

/// Callbacks pour les événements de fin de traitement
pub struct ProcessingCallbacks {
    /// Job ID
    pub job_id: Uuid,
    /// Track ID
    pub track_id: Uuid,
    /// Pool de connexions DB
    pub db: PgPool,
    /// Répertoire de sortie
    pub output_dir: PathBuf,
}

impl ProcessingCallbacks {
    /// Crée un nouveau gestionnaire de callbacks
    pub fn new(job_id: Uuid, track_id: Uuid, db: PgPool, output_dir: PathBuf) -> Self {
        Self {
            job_id,
            track_id,
            db,
            output_dir,
        }
    }

    /// Callback appelé en cas de succès
    pub async fn on_success(&self) -> Result<(), AppError> {
        tracing::info!(
            "✅ Job {} terminé avec succès pour track {}",
            self.job_id,
            self.track_id
        );

        // Mettre à jour le statut en DB
        sqlx::query!(
            r#"
            UPDATE stream_jobs
            SET status = 'done', updated_at = NOW()
            WHERE id = $1
            "#,
            self.job_id
        )
        .execute(&self.db)
        .await
        .map_err(|e| AppError::InternalError {
            message: format!("Failed to update job status: {}", e),
        })?;

        // Vérifier que le manifest existe
        let manifest_path = self.output_dir.join("index.m3u8");
        if !manifest_path.exists() {
            tracing::warn!(
                "⚠️ Manifest HLS non trouvé pour job {}: {}",
                self.job_id,
                manifest_path.display()
            );
        } else {
            tracing::debug!(
                "✅ Manifest HLS généré: {}",
                manifest_path.display()
            );
        }

        Ok(())
    }

    /// Callback appelé en cas d'erreur
    pub async fn on_error(&self, error_message: &str) -> Result<(), AppError> {
        tracing::error!(
            "❌ Job {} échoué pour track {}: {}",
            self.job_id,
            self.track_id,
            error_message
        );

        // Mettre à jour le statut avec l'erreur
        sqlx::query!(
            r#"
            UPDATE stream_jobs
            SET status = 'error', error_message = $1, updated_at = NOW()
            WHERE id = $2
            "#,
            error_message,
            self.job_id
        )
        .execute(&self.db)
        .await
        .map_err(|e| AppError::InternalError {
            message: format!("Failed to update job status with error: {}", e),
        })?;

        // Nettoyer les fichiers temporaires (optionnel)
        // On garde les fichiers pour debug, mais on pourrait les supprimer ici

        Ok(())
    }

    /// Callback appelé en cas de timeout
    pub async fn on_timeout(&self) -> Result<(), AppError> {
        tracing::warn!(
            "⏱️ Job {} expiré (timeout) pour track {}",
            self.job_id,
            self.track_id
        );

        // Mettre à jour le statut
        let error_msg = format!("Encoding timed out for job {}", self.job_id);
        self.on_error(&error_msg).await?;

        Ok(())
    }

    /// Finalise la playlist HLS pour VOD
    /// Cette fonction peut être appelée après on_success pour s'assurer
    /// que la playlist est complète et valide
    pub async fn finalize_playlist(&self) -> Result<(), AppError> {
        let manifest_path = self.output_dir.join("index.m3u8");
        
        if !manifest_path.exists() {
            return Err(AppError::NotFound {
                resource: format!("HLS manifest for job {}", self.job_id),
            });
        }

        // Lire le manifest
        let content = tokio::fs::read_to_string(&manifest_path).await
            .map_err(|e| AppError::InternalError {
                message: format!("Failed to read manifest: {}", e),
            })?;

        // Vérifier que le manifest contient #EXT-X-ENDLIST (pour VOD)
        if !content.contains("#EXT-X-ENDLIST") {
            tracing::warn!(
                "⚠️ Manifest HLS pour job {} ne contient pas #EXT-X-ENDLIST (VOD requis)",
                self.job_id
            );
            
            // Ajouter #EXT-X-ENDLIST si manquant
            let mut updated_content = content;
            if !updated_content.ends_with('\n') {
                updated_content.push('\n');
            }
            updated_content.push_str("#EXT-X-ENDLIST\n");
            
            tokio::fs::write(&manifest_path, updated_content).await
                .map_err(|e| AppError::InternalError {
                    message: format!("Failed to update manifest: {}", e),
                })?;
            
            tracing::info!("✅ #EXT-X-ENDLIST ajouté au manifest pour job {}", self.job_id);
        }

        Ok(())
    }
}

