use crate::app_config::AppConfig;
use crate::server::custom_error::HttpResult;
use crate::utils::time_utils::current_day;
use actix_web::HttpResponse;
use anyhow::Context;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use walkdir::WalkDir;
use zip::write::SimpleFileOptions;

/// Download a full copy of the storage data
pub async fn download_storage() -> HttpResult {
    let mut zip_buff = Cursor::new(Vec::new());
    let mut zip = zip::ZipWriter::new(&mut zip_buff);

    let options = SimpleFileOptions::default()
        .compression_method(zip::CompressionMethod::Bzip2)
        .unix_permissions(0o700);

    let storage = AppConfig::get().storage_path();

    let mut file_buff = Vec::new();
    for entry in WalkDir::new(&storage) {
        let entry = entry?;

        let path = entry.path();
        let name = path.strip_prefix(&storage).unwrap();
        let path_as_string = name
            .to_str()
            .map(str::to_owned)
            .with_context(|| format!("{name:?} Is a Non UTF-8 Path"))?;

        // Write file or directory explicitly
        // Some unzip tools unzip files with directory paths correctly, some do not!
        if path.is_file() {
            log::debug!("adding file {path:?} as {name:?} ...");
            zip.start_file(path_as_string, options)?;
            let mut f = File::open(path)?;

            f.read_to_end(&mut file_buff)?;
            zip.write_all(&file_buff)?;
            file_buff.clear();
        } else if !name.as_os_str().is_empty() {
            // Only if not root! Avoids path spec / warning
            // and mapname conversion failed error on unzip
            log::debug!("adding dir {path_as_string:?} as {name:?} ...");
            zip.add_directory(path_as_string, options)?;
        }
    }

    // Inject runtime configuration
    zip.start_file("/app_config.json", options)?;
    zip.write_all(&serde_json::to_vec_pretty(&AppConfig::get())?)?;

    zip.finish()?;

    let filename = format!("storage-{}.zip", current_day());

    Ok(HttpResponse::Ok()
        .content_type("application/zip")
        .insert_header((
            "content-disposition",
            format!("attachment; filename=\"{filename}\""),
        ))
        .body(zip_buff.into_inner()))
}