use config::AppConfig;
use std::path::PathBuf;

#[derive(Clone)]
pub struct AppStorage {
    pub base_path: PathBuf,
    pub public_url_base: String,
}

impl AppStorage {
    pub fn new(config: &AppConfig) -> anyhow::Result<Self> {
        let base_path = config
            .env
            .get("STORAGE_PATH")
            .map(PathBuf::from)
            .unwrap_or_else(|| PathBuf::from("/data/files"));

        let public_url_base = config
            .env
            .get("STORAGE_PUBLIC_URL")
            .cloned()
            .unwrap_or_else(|| "/files".to_string());

        Ok(Self {
            base_path,
            public_url_base,
        })
    }

    /// Write data to a local path and return the public URL.
    pub async fn upload(
        &self,
        key: &str,
        data: Vec<u8>,
    ) -> anyhow::Result<String> {
        let path = self.base_path.join(key);

        // Create parent directories
        if let Some(parent) = path.parent() {
            tokio::fs::create_dir_all(parent).await?;
        }

        tokio::fs::write(&path, &data).await?;

        let url = format!(
            "{}/{}",
            self.public_url_base.trim_end_matches('/'),
            key
        );
        Ok(url)
    }

    pub async fn delete(&self, key: &str) -> anyhow::Result<()> {
        let path = self.base_path.join(key);
        if path.exists() {
            tokio::fs::remove_file(&path).await?;
        }
        Ok(())
    }

    /// Read a file by key and return (bytes, content_type).
    pub async fn read(&self, key: &str) -> anyhow::Result<(Vec<u8>, String)> {
        let path = self.base_path.join(key);
        let data = tokio::fs::read(&path).await?;
        let content_type = mime_guess2::from_path(&path)
            .first_or_octet_stream()
            .to_string();
        Ok((data, content_type))
    }
}
