use btrfs::structs::{ExtentData, ObjectType};
use btrfs::v2::error::Result;
use btrfs::v2::tree::PartialKey;
use btrfs::v2::volume::*;
use include_blob::include_blob;

fn open_filesystem() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
    let filesystem_data = include_blob!("simple.img").as_slice();

    let volume = Volume::new(filesystem_data)?.into_volume2()?;

    Ok(volume)
}

fn open_filesystem_lzo() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
    let filesystem_data = include_blob!("compressed-lzo.img").as_slice();

    let volume = Volume::new(filesystem_data)?.into_volume2()?;

    Ok(volume)
}

fn open_filesystem_zlib() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
    let filesystem_data = include_blob!("compressed-zlib.img").as_slice();

    let volume = Volume::new(filesystem_data)?.into_volume2()?;

    Ok(volume)
}

fn open_filesystem_zstd() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
    let filesystem_data = include_blob!("compressed-zstd.img").as_slice();

    let volume = Volume::new(filesystem_data)?.into_volume2()?;

    Ok(volume)
}

#[test_log::test]
fn asdf() -> Result<()> {
    let a = open_filesystem()?;
    Ok(())
}

#[test_log::test]
fn read_superblock() -> Result<()> {
    let vol2 = open_filesystem()?;
    let sb = vol2.inner.superblock();
    println!("{sb:#?}");

    assert!(sb.verify_magic());
    assert!(sb.verify_checksum());

    Ok(())
}

#[test_log::test]
fn iter_roots() -> Result<()> {
    let vol2 = open_filesystem()?;
    for (id, _) in vol2.roots.iter() {
        log::info!("[{id:?}] ");
    }

    Ok(())
}

#[test_log::test]
fn iter_roots_rev() -> Result<()> {
    let vol2 = open_filesystem()?;
    for (id, _) in vol2.roots.iter().rev() {
        log::info!("[{id:?}] ");
    }

    Ok(())
}

#[test_log::test]
fn iter_default_subvol() -> Result<()> {
    let v2 = open_filesystem()?;
    let fs = v2.default_subvolume().expect("default subvol");

    log::info!("files 1:");
    let now = std::time::Instant::now();
    for (_id, entry) in fs.fs_root().iter() {
        if let Some(_dir) = entry.as_dir_index() {
            //log::info!("{}", dir.name_as_string_lossy());
        }
    }
    log::info!("files 1: [took {}ms]", now.elapsed().as_millis());

    log::info!("files 2:");
    let now = std::time::Instant::now();
    for (_id, entry) in fs.fs_root().iter() {
        if let Some(_dir) = entry.as_dir_index() {
            //log::info!("{}", dir.name_as_string_lossy());
        }
    }
    log::info!("files 2: [took {}ms]", now.elapsed().as_millis());
    Ok(())
}

#[test_log::test]
fn get_inode_items() -> Result<()> {
    let v2 = open_filesystem()?;
    let fs = v2.default_subvolume().expect("default subvol");

    let search_key = PartialKey::new(Some(fs.root_dir_id()), Some(ObjectType::DirIndex), None);

    // with range

    log::info!("range:");
    for (key, v) in fs.fs_root().find_range(&search_key)? {
        let dirindex = v.as_dir_index().unwrap();
        let inode_id: u64 = dirindex.item().location.id().into();
        log::info!("[{key:?}] {v:#?}");
        log::info!("inode: {inode_id}");

        let inode_item = fs.get_inode_item(inode_id)?;
        log::info!("inode: {inode_item:#?}");
        let extents = fs.get_inode_extents(inode_id)?;

        for (_, extent) in extents {
            match extent {
                ExtentData::Inline { header, data } => {
                    log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
                }
                _ => {}
            }
        }
    }
    log::info!("range: [end]");

    Ok(())
}

#[test_log::test]
fn find_file() -> Result<()> {
    let v2 = open_filesystem()?;
    let fs = v2.default_subvolume().expect("default subvol");

    let root_dir = fs.get_root_dir();
    let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
    log::info!("chidlren: {:?}", children);

    let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
    let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
    assert_eq!(
        &file_contents[..52],
        b"                   GNU LESSER GENERAL PUBLIC LICENSE"
    );
    log::info!("license file:");
    log::info!("{}", String::from_utf8_lossy(&file_contents));

    Ok(())
}

#[test_log::test]
fn find_file_zlib() -> Result<()> {
    let v2 = open_filesystem_zlib()?;
    let fs = v2.default_subvolume().expect("default subvol");

    let root_dir = fs.get_root_dir();
    let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
    log::info!("chidlren: {:?}", children);

    let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
    let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
    //assert_eq!(&file_contents[..11], b"hello world");
    log::info!("license file:");
    log::info!("{}", String::from_utf8_lossy(&file_contents));
    assert_eq!(
        &file_contents[..52],
        b"                   GNU LESSER GENERAL PUBLIC LICENSE"
    );

    Ok(())
}

#[test_log::test]
fn find_file_lzo() -> Result<()> {
    let v2 = open_filesystem_lzo()?;
    let fs = v2.default_subvolume().expect("default subvol");

    let root_dir = fs.get_root_dir();
    let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
    log::info!("chidlren: {:?}", children);

    let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
    let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
    assert_eq!(
        &file_contents[..52],
        b"                   GNU LESSER GENERAL PUBLIC LICENSE"
    );
    log::info!("license file:");
    log::info!("{}", String::from_utf8_lossy(&file_contents));

    Ok(())
}

#[test_log::test]
fn find_file_zstd() -> Result<()> {
    let v2 = open_filesystem_zstd()?;
    let fs = v2.default_subvolume().expect("default subvol");

    let root_dir = fs.get_root_dir();
    let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
    log::info!("chidlren: {:?}", children);

    let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
    let file_contents = fs.read_inode_raw(&cmake_list, ..).expect("file contents");
    assert_eq!(
        &file_contents[..52],
        b"                   GNU LESSER GENERAL PUBLIC LICENSE"
    );
    log::info!("license file:");
    log::info!("{}", String::from_utf8_lossy(&file_contents));

    Ok(())
}