//! Temporary file management utilities

use std::path::{Path, PathBuf};
use std::collections::HashSet;
use uuid::Uuid;
use crate::core::TvaiError;

/// Manages temporary files for processing operations
#[derive(Debug)]
pub struct TempFileManager {
    base_dir: PathBuf,
    operation_files: HashSet<PathBuf>,
    auto_cleanup: bool,
}

impl TempFileManager {
    /// Create a new temporary file manager
    pub fn new(base_dir: Option<&Path>) -> Result<Self, TvaiError> {
        let base_dir = match base_dir {
            Some(dir) => dir.to_path_buf(),
            None => {
                let temp_dir = std::env::temp_dir().join("tvai_temp");
                std::fs::create_dir_all(&temp_dir)?;
                temp_dir
            }
        };

        // Ensure base directory exists
        std::fs::create_dir_all(&base_dir)?;

        Ok(Self {
            base_dir,
            operation_files: HashSet::new(),
            auto_cleanup: true,
        })
    }

    /// Set whether to automatically cleanup files on drop
    pub fn set_auto_cleanup(&mut self, auto_cleanup: bool) {
        self.auto_cleanup = auto_cleanup;
    }

    /// Get the base temporary directory
    pub fn base_dir(&self) -> &Path {
        &self.base_dir
    }

    /// Create a temporary file path for an operation
    pub fn create_temp_path(&mut self, operation_id: &str, suffix: &str) -> PathBuf {
        let filename = format!("{}_{}", operation_id, suffix);
        let path = self.base_dir.join(filename);
        self.operation_files.insert(path.clone());
        path
    }

    /// Create a temporary file path with a random UUID
    pub fn create_unique_temp_path(&mut self, suffix: &str) -> PathBuf {
        let operation_id = Uuid::new_v4().to_string();
        self.create_temp_path(&operation_id, suffix)
    }

    /// Create a temporary directory for an operation
    pub fn create_temp_dir(&mut self, operation_id: &str) -> Result<PathBuf, TvaiError> {
        let dir_path = self.base_dir.join(format!("{}_dir", operation_id));
        std::fs::create_dir_all(&dir_path)?;
        self.operation_files.insert(dir_path.clone());
        Ok(dir_path)
    }

    /// Register an existing file for cleanup
    pub fn register_file(&mut self, path: PathBuf) {
        self.operation_files.insert(path);
    }

    /// Clean up files for a specific operation
    pub fn cleanup_operation(&mut self, operation_id: &str) -> Result<(), TvaiError> {
        let pattern = format!("{}_", operation_id);
        let mut to_remove = Vec::new();

        for path in &self.operation_files {
            if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
                if filename.starts_with(&pattern) {
                    to_remove.push(path.clone());
                }
            }
        }

        for path in to_remove {
            self.remove_file_or_dir(&path)?;
            self.operation_files.remove(&path);
        }

        Ok(())
    }

    /// Clean up all temporary files
    pub fn cleanup_all(&mut self) -> Result<(), TvaiError> {
        let files_to_remove: Vec<_> = self.operation_files.iter().cloned().collect();

        for path in files_to_remove {
            self.remove_file_or_dir(&path)?;
        }

        self.operation_files.clear();
        Ok(())
    }

    /// Remove a file or directory
    fn remove_file_or_dir(&self, path: &Path) -> Result<(), TvaiError> {
        if path.exists() {
            if path.is_dir() {
                std::fs::remove_dir_all(path)?;
            } else {
                std::fs::remove_file(path)?;
            }
        }
        Ok(())
    }

    /// Get the number of tracked files
    pub fn file_count(&self) -> usize {
        self.operation_files.len()
    }

    /// Check if a file is being tracked
    pub fn is_tracked(&self, path: &Path) -> bool {
        self.operation_files.contains(path)
    }

    /// Get all tracked file paths
    pub fn tracked_files(&self) -> Vec<&PathBuf> {
        self.operation_files.iter().collect()
    }
}

impl Drop for TempFileManager {
    fn drop(&mut self) {
        if self.auto_cleanup {
            let _ = self.cleanup_all();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;
    use std::fs;

    #[test]
    fn test_temp_file_manager_creation() {
        let temp_dir = TempDir::new().unwrap();
        let manager = TempFileManager::new(Some(temp_dir.path())).unwrap();

        assert_eq!(manager.base_dir(), temp_dir.path());
        assert_eq!(manager.file_count(), 0);
    }

    #[test]
    fn test_create_temp_path() {
        let temp_dir = TempDir::new().unwrap();
        let mut manager = TempFileManager::new(Some(temp_dir.path())).unwrap();

        let path = manager.create_temp_path("test_op", "input.mp4");

        assert!(path.to_string_lossy().contains("test_op_input.mp4"));
        assert_eq!(manager.file_count(), 1);
        assert!(manager.is_tracked(&path));
    }

    #[test]
    fn test_cleanup_operation() {
        let temp_dir = TempDir::new().unwrap();
        let mut manager = TempFileManager::new(Some(temp_dir.path())).unwrap();

        // Create files for different operations
        let path1 = manager.create_temp_path("op1", "file1.txt");
        let path2 = manager.create_temp_path("op1", "file2.txt");
        let path3 = manager.create_temp_path("op2", "file3.txt");

        // Create actual files
        fs::write(&path1, "test1").unwrap();
        fs::write(&path2, "test2").unwrap();
        fs::write(&path3, "test3").unwrap();

        assert_eq!(manager.file_count(), 3);

        // Clean up op1 files
        manager.cleanup_operation("op1").unwrap();

        assert_eq!(manager.file_count(), 1);
        assert!(!path1.exists());
        assert!(!path2.exists());
        assert!(path3.exists());
    }
}
