use gix::error::ResultExt;

use crate::{
    bare::GitBare,
    cmd::archive::{ArchiveOptions, ArchiveResult},
    errors::{GitError, GitResult},
};

impl GitBare {
    pub fn archive_zip(
        &self,
        options: ArchiveOptions,
    ) -> GitResult<ArchiveResult> {
        let repo = self.gix_repo()?;
        let gix_id: gix::hash::ObjectId = (&options.tree).try_into()?;
        let tree_id = {
            let gix_id = gix_id;
            if let Ok(tree) = repo.find_tree(gix_id) {
                tree.id().detach()
            } else {
                let commit = repo
                    .find_commit(gix_id)
                    .map_err(|e| GitError::Gix(e.to_string()))?;
                commit
                    .tree_id()
                    .map_err(|e| GitError::Gix(e.to_string()))?
                    .detach()
            }
        };

        let (mut stream, _index) = repo
            .worktree_stream(tree_id)
            .map_err(|e| GitError::Gix(e.to_string()))?;

        let mut buf: Vec<u8> = Vec::new();

        let archive_opts = gix_archive::Options {
            format: gix_archive::Format::Zip {
                compression_level: None,
            },
            tree_prefix: options.prefix.map(|p| gix::bstr::BString::from(p)),
            ..Default::default()
        };

        gix_archive::write_stream_seek(
            &mut stream,
            |stream| stream.next_entry().or_erased(),
            std::io::Cursor::new(&mut buf),
            archive_opts,
        )
        .map_err(|e| GitError::Gix(e.to_string()))?;

        Ok(ArchiveResult { bytes: buf })
    }
}
