use parking_lot::Mutex;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use uuid::Uuid;

const TEMP_DIR_NAME: &str = "hikari-uploads";

pub struct TempFileManager {
    base_dir: PathBuf,
    files: HashMap<String, Vec<PathBuf>>,
}

impl TempFileManager {
    pub fn new() -> Result<Self, String> {
        let base_dir = std::env::temp_dir().join(TEMP_DIR_NAME);

        if !base_dir.exists() {
            fs::create_dir_all(&base_dir)
                .map_err(|e| format!("Failed to create temp directory: {}", e))?;
        }

        Ok(TempFileManager {
            base_dir,
            files: HashMap::new(),
        })
    }

    #[allow(dead_code)]
    pub fn get_base_dir(&self) -> &Path {
        &self.base_dir
    }

    pub fn save_file(
        &mut self,
        conversation_id: &str,
        data: &[u8],
        original_filename: Option<&str>,
    ) -> Result<PathBuf, String> {
        let unique_id = Uuid::new_v4();
        let extension = original_filename
            .and_then(|name| Path::new(name).extension())
            .and_then(|ext| ext.to_str())
            .unwrap_or("bin");

        let filename = format!("{}_{}.{}", conversation_id, unique_id, extension);
        let file_path = self.base_dir.join(&filename);

        fs::write(&file_path, data)
            .map_err(|e| format!("Failed to write temp file: {}", e))?;

        self.files
            .entry(conversation_id.to_string())
            .or_default()
            .push(file_path.clone());

        Ok(file_path)
    }

    pub fn register_file(&mut self, conversation_id: &str, file_path: PathBuf) {
        self.files
            .entry(conversation_id.to_string())
            .or_default()
            .push(file_path);
    }

    pub fn get_files_for_conversation(&self, conversation_id: &str) -> Vec<PathBuf> {
        self.files
            .get(conversation_id)
            .cloned()
            .unwrap_or_default()
    }

    pub fn cleanup_conversation(&mut self, conversation_id: &str) -> Result<(), String> {
        if let Some(files) = self.files.remove(conversation_id) {
            for file_path in files {
                if file_path.exists() {
                    if let Err(e) = fs::remove_file(&file_path) {
                        eprintln!(
                            "Warning: Failed to remove temp file {:?}: {}",
                            file_path, e
                        );
                    }
                }
            }
        }
        Ok(())
    }

    pub fn cleanup_all(&mut self) -> Result<(), String> {
        let conversation_ids: Vec<String> = self.files.keys().cloned().collect();

        for conversation_id in conversation_ids {
            self.cleanup_conversation(&conversation_id)?;
        }

        Ok(())
    }

    pub fn cleanup_orphaned_files(&mut self) -> Result<usize, String> {
        let mut cleaned_count = 0;

        if !self.base_dir.exists() {
            return Ok(0);
        }

        let tracked_files: std::collections::HashSet<PathBuf> =
            self.files.values().flatten().cloned().collect();

        let entries = fs::read_dir(&self.base_dir)
            .map_err(|e| format!("Failed to read temp directory: {}", e))?;

        for entry in entries.flatten() {
            let path = entry.path();
            if path.is_file() && !tracked_files.contains(&path) {
                if let Err(e) = fs::remove_file(&path) {
                    eprintln!("Warning: Failed to remove orphaned file {:?}: {}", path, e);
                } else {
                    cleaned_count += 1;
                }
            }
        }

        Ok(cleaned_count)
    }
}

impl Default for TempFileManager {
    fn default() -> Self {
        Self::new().expect("Failed to create TempFileManager")
    }
}

pub type SharedTempFileManager = Arc<Mutex<TempFileManager>>;

pub fn create_shared_temp_manager() -> Result<SharedTempFileManager, String> {
    Ok(Arc::new(Mutex::new(TempFileManager::new()?)))
}
