#[allow(warnings)]
#[cfg(feature = "docx")]
pub mod docx {
    use std::{fs::File, io::Read, path::PathBuf};

    use zip::ZipArchive;

    /// docx_replace
    ///
    /// finds all 'name' and replace with 'andrew'
    /// ```ignore
    ///use doe::*;
    ///docx::docx_replace("./name.docx","name","andrew").unwrap();
    /// ```
    pub fn docx_replace<T>(
        docx_path: PathBuf,
        target: T,
        new_target: T,
    ) -> Result<(), Box<dyn std::error::Error>>
    where
    T: ToString,
    {
        use std::fs::File;
        use std::io::prelude::*;
        use zip::read::ZipArchive;
        use zip::write::FileOptions;
        use zip::CompressionMethod;

        // Open the .docx file as a zip
        let file = File::open(&docx_path)?;
        let mut archive = ZipArchive::new(file)?;
        let new_archive_path = "new_archive.zip";

        let options = FileOptions::default()
            .compression_method(CompressionMethod::Stored)
            .unix_permissions(0o755);

        let file = File::create(&new_archive_path).unwrap();
        let mut new_zip = zip::ZipWriter::new(file);

        // Loop over all of the files in the .docx archive
        for i in 0..archive.len() {
            let mut file_in_archive = archive.by_index(i).unwrap();
            let file_in_archive_name = &file_in_archive.name();
            if file_in_archive_name.to_string() == "word/document.xml" {
                let mut contents = String::new();
                file_in_archive.read_to_string(&mut contents).unwrap();
                // Perform text replacement
                let new_contents = contents.replace(&target.to_string(), &new_target.to_string());
                new_zip.start_file("word/document.xml", options)?;
                new_zip.write_all(new_contents.as_bytes())?;
            } else {
                new_zip.start_file(file_in_archive_name.to_string(), options)?;
                let mut buffer = Vec::new();
                file_in_archive.read_to_end(&mut buffer)?;
                new_zip.write_all(&buffer)?;
            }
        }
        std::fs::rename(new_archive_path, docx_path).unwrap();
        Ok(())
    }
   

    pub fn docx_bytes_replace_values<T>(
        docx_bytes: Vec<u8>,
        values: Vec<(T, T)>,
    ) -> Result<Vec<u8>, Box<dyn std::error::Error>>
    where
        T: ToString,
    {
        use std::fs::File;
        use std::io::{Read, Write, Cursor};
        use std::path::PathBuf;
        use zip::read::ZipArchive;
        use zip::write::FileOptions;
        use zip::CompressionMethod;
        // Open the .docx file as a zip
        let file =  Cursor::new(docx_bytes);
        let mut archive = ZipArchive::new(file)?;
    
        let options = FileOptions::default()
            .compression_method(CompressionMethod::Stored)
            .unix_permissions(0o755);
    
        // Create a new ZIP archive in memory
        let mut new_zip_data = Cursor::new(Vec::new());
        let mut new_zip = zip::ZipWriter::new(&mut new_zip_data);
    
        // Loop over all of the files in the .docx archive
        for i in 0..archive.len() {
            let mut file_in_archive = archive.by_index(i)?;
            let file_in_archive_name = file_in_archive.name().to_string();
    
            if file_in_archive_name.ends_with(".xml") {
                let mut contents = String::new();
                file_in_archive.read_to_string(&mut contents)?;
    
                let mut new_contents = contents;
                for (target, new_target) in &values {
                    // Perform text replacement
                    new_contents = new_contents.replace(&target.to_string(), &new_target.to_string());
                }
    
                new_zip.start_file(&file_in_archive_name, options)?;
                new_zip.write_all(new_contents.as_bytes())?;
            } else {
                new_zip.start_file(&file_in_archive_name, options)?;
                let mut buffer = Vec::new();
                file_in_archive.read_to_end(&mut buffer)?;
                new_zip.write_all(&buffer)?;
            }
        }
    
        // Finalize the new zip archive
        new_zip.finish()?;
    
        // Explicitly drop `new_zip` to release the mutable borrow on `new_zip_data`
        drop(new_zip);
    
        Ok(new_zip_data.into_inner().to_vec())
    }

    

    ///```ignore
    /// use doe::*;
    /// docx::docx_replace_values("./rust.docx", &[("[name]","andrew")]).unwrap();
    /// ```
    pub fn docx_replace_values<T>(
        docx_path: PathBuf,
        values: Vec<(T, T)>,
    ) -> Result<(), Box<dyn std::error::Error>>
    where
        T: ToString,
    {
        use std::fs::File;
        use std::io::{Read, Write, Cursor};
        use std::path::PathBuf;
        use zip::read::ZipArchive;
        use zip::write::FileOptions;
        use zip::CompressionMethod;
        // Open the .docx file as a zip
        let file = File::open(&docx_path)?;
        let mut archive = ZipArchive::new(file)?;
    
        let options = FileOptions::default()
            .compression_method(CompressionMethod::Stored)
            .unix_permissions(0o755);
    
        // Create a new ZIP archive in memory
        let mut new_zip_data = Cursor::new(Vec::new());
        let mut new_zip = zip::ZipWriter::new(&mut new_zip_data);
    
        // Loop over all of the files in the .docx archive
        for i in 0..archive.len() {
            let mut file_in_archive = archive.by_index(i)?;
            let file_in_archive_name = file_in_archive.name().to_string();
    
            if file_in_archive_name.ends_with(".xml") {
                let mut contents = String::new();
                file_in_archive.read_to_string(&mut contents)?;
    
                let mut new_contents = contents;
                for (target, new_target) in &values {
                    // Perform text replacement
                    new_contents = new_contents.replace(&target.to_string(), &new_target.to_string());
                }
    
                new_zip.start_file(&file_in_archive_name, options)?;
                new_zip.write_all(new_contents.as_bytes())?;
            } else {
                new_zip.start_file(&file_in_archive_name, options)?;
                let mut buffer = Vec::new();
                file_in_archive.read_to_end(&mut buffer)?;
                new_zip.write_all(&buffer)?;
            }
        }
    
        // Finalize the new zip archive
        new_zip.finish()?;
    
        // Explicitly drop `new_zip` to release the mutable borrow on `new_zip_data`
        drop(new_zip);
    
        // Replace the original .docx file with the new one
        let mut file = File::create(&docx_path)?;
        file.write_all(&new_zip_data.into_inner())?;
    
        Ok(())
    }
    ///```ignore
    /// use doe::*;
    /// docx::docx_replace_values("./rust.docx", &[("[name]","andrew")]).unwrap();
    /// ```
    pub fn docx_replace_values_old<T>(
        docx_path: PathBuf,
        values: Vec<(T, T)>,
    ) -> Result<(), Box<dyn std::error::Error>> 
    where
    T: ToString,
    {
        use std::fs::File;
        use std::io::prelude::*;
        use zip::read::ZipArchive;
        use zip::write::FileOptions;
        use zip::CompressionMethod;

        // Open the .docx file as a zip
        let file = File::open(docx_path.clone())?;
        let mut archive = ZipArchive::new(file)?;
        let new_archive_path = "new_archive.zip";

        let options = FileOptions::default()
            .compression_method(CompressionMethod::Stored)
            .unix_permissions(0o755);

        let file = File::create(&new_archive_path).unwrap();
        let mut new_zip = zip::ZipWriter::new(file);

        // Loop over all of the files in the .docx archive
        for i in 0..archive.len() {
            let mut file_in_archive = archive.by_index(i).unwrap();
            let file_in_archive_name = &file_in_archive.name().to_string();
            if file_in_archive_name.to_string().ends_with(".xml"){
                let mut contents = String::new();
                file_in_archive.read_to_string(&mut contents).unwrap();
                let mut new_contents = contents.to_string();
                for value in &values {
                    let (target, new_target) = value;
                    // Perform text replacement
                    new_contents = new_contents.replace(&target.to_string(), &new_target.to_string());
                }
                new_zip.start_file(file_in_archive_name, options)?;
                new_zip.write_all(new_contents.as_bytes())?;
            } else {
                new_zip.start_file(file_in_archive_name.to_string(), options)?;
                let mut buffer = Vec::new();
                file_in_archive.read_to_end(&mut buffer)?;
                new_zip.write_all(&buffer)?;
            }
        }
        std::fs::rename(new_archive_path, docx_path).unwrap();
        Ok(())
    }

    pub fn docx_replace_values_save(
        docx_path: PathBuf,
        values: Vec<(String, String)>,
        new_docx_path: PathBuf,
    ) -> Result<(), Box<dyn std::error::Error>> {
        use std::fs::File;
        use std::io::prelude::*;
        use zip::read::ZipArchive;
        use zip::write::FileOptions;
        use zip::CompressionMethod;

        // Open the .docx file as a zip
        let file = File::open(docx_path.clone())?;
        let mut archive = ZipArchive::new(file)?;
        let new_archive_path = "new_archive.zip";

        let options = FileOptions::default()
            .compression_method(CompressionMethod::Stored)
            .unix_permissions(0o755);

        let file = File::create(&new_archive_path).unwrap();
        let mut new_zip = zip::ZipWriter::new(file);

        // Loop over all of the files in the .docx archive
        for i in 0..archive.len() {
            let mut file_in_archive = archive.by_index(i).unwrap();
            let file_in_archive_name = &file_in_archive.name();
            if file_in_archive_name.to_string() == "word/document.xml" {
                let mut contents = String::new();
                file_in_archive.read_to_string(&mut contents).unwrap();
                let mut new_contents = contents.to_string();
                for value in &values {
                    let (target, new_target) = value;
                    // Perform text replacement
                    new_contents = new_contents.replace(target, &new_target);
                }
                new_zip.start_file("word/document.xml", options)?;
                new_zip.write_all(new_contents.as_bytes())?;
            } else {
                new_zip.start_file(file_in_archive_name.to_string(), options)?;
                let mut buffer = Vec::new();
                file_in_archive.read_to_end(&mut buffer)?;
                new_zip.write_all(&buffer)?;
            }
        }
        std::fs::rename(new_archive_path, new_docx_path).unwrap();
        Ok(())
    }

    pub fn docx_get_xml(docx_path: impl AsRef<str>) -> Result<String, Box<dyn std::error::Error>> {
        let reader = File::open(docx_path.as_ref())?;
        let mut zip = ZipArchive::new(reader)?;
        let mut file = zip.by_name("word/document.xml")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(contents)
    }
    #[cfg(target_os = "unix")]
    pub fn docx_remove_read_only(
        docx_path: impl AsRef<str>,
    ) -> Result<(), Box<dyn std::error::Error>> {
        use std::fs::{metadata, set_permissions};
        use std::os::unix::fs::PermissionsExt;
        let docx_path = docx_path.as_ref();
        let meta = metadata(docx_path)?;
        let mut permissions = meta.permissions();
        let mode = permissions.mode();
        let new_mode = mode & !0o400;
        permissions.set_mode(new_mode);
        set_permissions(docx_path, permissions)?;
        Ok(())
    }

    pub fn parse_xml_get_content(xml: impl AsRef<str>) -> Vec<String> {
        use xml::reader::{EventReader, XmlEvent};
        let parser = EventReader::from_str(xml.as_ref());
        let mut content = vec![];
        for e in parser {
            match e {
                #[allow(warnings)]
                Ok(XmlEvent::StartElement { name, .. }) => {}
                #[allow(warnings)]
                Ok(XmlEvent::EndElement { name }) => {}
                Ok(XmlEvent::Characters(chars)) => {
                    content.push(chars);
                }
                Err(e) => {
                    println!("Error: {}", e);
                    break;
                }
                _ => {}
            }
        }
        content
    }
    pub fn docx_get_content(
        docx_path: impl AsRef<str>,
    ) -> Result<Vec<String>, Box<dyn std::error::Error>> {
        let xml = crate::docx::docx_get_xml(docx_path)?;
        Ok(crate::docx::parse_xml_get_content(xml))
    }
}

#[cfg(feature = "docx")]
pub use docx::*;
